hgg-frame 0.1.0.0 → 0.2.0.0
raw patch · 3 files changed
+102/−49 lines, 3 filesdep ~hgg-corePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: hgg-core
API changes (from Hackage documentation)
Files
- CHANGELOG.md +5/−0
- hgg-frame.cabal +4/−4
- src/Graphics/Hgg/Frame.hs +93/−45
CHANGELOG.md view
@@ -1,5 +1,10 @@ # Changelog for `hgg-frame` +## 0.2.0.0 — 2026-08-13++- Version lockstep with hgg-core 0.2 (no API changes).+- Bilingual (English / Japanese) haddock.+ ## 0.1.0.0 — 2026-07-18 First public release on Hackage.
hgg-frame.cabal view
@@ -1,19 +1,19 @@ cabal-version: 3.0 name: hgg-frame-version: 0.1.0.0+version: 0.2.0.0 extra-doc-files: CHANGELOG.md synopsis: DataFrame abstraction (class PlotData) and the df |>> spec binding for hgg description: A dataframe-independent abstraction for writing hgg plots as "dataframe + column names".- .+ * 'PlotData' — a typeclass that bridges any dataframe type to a 'Resolver' (column name to ColData). Zero-dependency instances for 'Map' and assoc-lists are included, so it works without any dataframe library. * '(|>>)' — binds a dataframe to a spec, producing a 'BoundPlot'. (The Hackage @dataframe@ package already uses @|>@, hence @|>>@.)- .+ Rendering wrappers (saveSVGBound etc.) live in the backend packages (hgg-svg etc.), so this package depends only on hgg-core. license: BSD-3-Clause@@ -38,7 +38,7 @@ , vector >= 0.13 && < 0.14 , text >= 2.0 && < 2.2 , containers >= 0.6 && < 0.8- , hgg-core ^>= 0.1+ , hgg-core ^>= 0.2 default-language: Haskell2010 test-suite hgg-frame-tests
src/Graphics/Hgg/Frame.hs view
@@ -1,25 +1,35 @@ -- | -- Module : Graphics.Hgg.Frame--- Description : DataFrame 抽象 (class PlotData) ─ 列名で図を書くための df 非依存橋+-- Description : DataFrame abstraction (class PlotData) — a df-agnostic bridge -- Copyright : (c) 2026 Aelysce Project (Toshiaki Honda) -- License : BSD-3-Clause ----- ggplot2 のように「データフレーム + 列名」 で図を書くための抽象。 Haskell に--- 統一 df ライブラリが無い事情に対応し、 **df 型に依存しない** typeclass--- 'PlotData' で「列名 → 実ベクタ」 (= 既存の 'Resolver') を取り出す。+-- [日本語]: ggplot2 のように「データフレーム + 列名」 で図を書くための抽象。+-- Haskell に統一 df ライブラリが無い事情に対応し、 __df 型に依存しない__+-- typeclass 'PlotData' で「列名 → 実ベクタ」 (= 既存の 'Resolver') を取り出す。 --+-- [English]: An abstraction for drawing figures from "a dataframe + column+-- names", in the style of ggplot2. Since Haskell has no single unified+-- dataframe library, the __dataframe-type-agnostic__ typeclass 'PlotData'+-- extracts "column name to actual vector" (that is, the existing 'Resolver').+-- -- @ -- import Graphics.Hgg.Easy (scatter, layer) -- import Graphics.Hgg.Frame -- import qualified Data.Map.Strict as M -- -- df = M.fromList [(\"x\", inline [1,2,3]), (\"y\", inline [4,5,6])]--- -- df |>> layer (scatter \"x\" \"y\") -- バインドは A3 ((|>>)) で+-- -- df |>> layer (scatter \"x\" \"y\") -- @ ----- 本 module はゼロ依存 instance (assoc-list / 'Data.Map.Map') のみを持つ。--- Hackage @dataframe@ 等の外部 df 型の instance は各橋 package が所有する+-- [日本語]: 本 module はゼロ依存 instance (assoc-list / 'Data.Map.Map') のみを+-- 持つ。 Hackage @dataframe@ 等の外部 df 型の instance は各橋 package が所有する -- (orphan 回避、 proposal spec-2 §3.1)。+--+-- [English]: This module carries only the zero-dependency instances+-- (assoc-list / 'Data.Map.Map'). Instances for external dataframe types such+-- as Hackage @dataframe@ are owned by the respective bridge packages (to+-- avoid orphans; proposal spec-2 §3.1). {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-}@@ -47,24 +57,37 @@ -- class PlotData (リッチ版: toResolver + columnNames + nrows) -- =========================================================================== --- | 任意の df 型を hgg の描画系に橋渡しする typeclass。------ リッチ版 (proposal spec-2 §3.1 のユーザ決定): 'toResolver' だけでも描画は--- できるが、 'columnNames' で **バインド時の列存在検証** (Phase 14 A5) と将来の--- pairs / auto-aes が、 'nrows' で **空 df 検出**が可能になる。+-- | [日本語]: 任意の df 型を hgg の描画系に橋渡しする typeclass。+-- リッチ版 (proposal spec-2 §3.1 のユーザ決定): 'toResolver' だけでも描画は+-- できるが、 'columnNames' で __バインド時の列存在検証__ と将来の+-- pairs / auto-aes が、 'nrows' で __空 df 検出__ が可能になる。+-- [English]: Bridges an arbitrary dataframe type to the plotting system.+-- 'toResolver' alone is enough to draw, but 'columnNames' additionally+-- enables __column-existence checking at bind time__ (and future+-- pairs / auto-aes), while 'nrows' enables __empty-dataframe detection__. class PlotData df where- -- | 列名 → 'ColData'。 既存の 'Resolver' 型をそのまま再利用する+ -- | [日本語]: 列名 → 'ColData'。 既存の 'Resolver' 型をそのまま再利用する -- (= @saveSVG@ 等が第 2 引数に取るのと同じ「実質 df」)。+ -- [English]: Column name to 'ColData'. Reuses the existing 'Resolver' type+ -- as-is (the same "effective dataframe" that @saveSVG@ takes as its+ -- second argument). toResolver :: df -> Resolver - -- | 全列名。 列存在チェック・将来の auto-aes / pairs に使う。+ -- | [日本語]: 全列名。 列存在チェック・将来の auto-aes / pairs に使う。+ -- [English]: All column names. Used for column-existence checks and for+ -- future auto-aes / pairs support. columnNames :: df -> [Text] - -- | 行数。 空 df (@nrows == 0@) 検出に使う。 列ごとに長さが違う場合は- -- **最長の列の長さ**を返す (= 描画が要求しうる最大 index)。+ -- | [日本語]: 行数。 空 df (@nrows == 0@) 検出に使う。 列ごとに長さが違う+ -- 場合は __最長の列の長さ__ を返す (= 描画が要求しうる最大 index)。+ -- [English]: Number of rows, used to detect an empty dataframe+ -- (@nrows == 0@). When columns differ in length, returns+ -- __the length of the longest column__ (the largest index that rendering+ -- may demand). nrows :: df -> Int --- | 'ColData' (数値列 / 文字列列) の要素数。+-- | [日本語]: 'ColData' (数値列 / 文字列列) の要素数。+-- [English]: Element count of a 'ColData' (a numeric or textual column). colLen :: ColData -> Int colLen (NumData v) = V.length v colLen (TxtData v) = V.length v@@ -73,13 +96,18 @@ -- ゼロ依存 instance ─ df ライブラリ無しでも使える最小実装 -- =========================================================================== --- | assoc-list (列名と列の対の並び)。 列名重複時は **先勝ち** ('lookup' 準拠)。+-- | [日本語]: assoc-list (列名と列の対の並び)。 列名重複時は __先勝ち__+-- ('lookup' 準拠)。+-- [English]: An association list of column name / column pairs. On duplicate+-- column names __the first wins__ (following 'lookup'). instance PlotData [(Text, ColData)] where toResolver pairs = \name -> lookup name pairs columnNames = map fst nrows pairs = maximum (0 : map (colLen . snd) pairs) --- | 'Data.Map.Strict.Map' 版。 列名重複は Map が解決済 (一意)。+-- | [日本語]: 'Data.Map.Strict.Map' 版。 列名重複は Map が解決済 (一意)。+-- [English]: The 'Data.Map.Strict.Map' variant. Duplicate column names are+-- already resolved by the map itself (keys are unique). instance PlotData (Map Text ColData) where toResolver m = \name -> M.lookup name m columnNames = M.keys@@ -89,35 +117,48 @@ -- バインド境界 ─ df を spec に結びつけた純値 'BoundPlot' -- =========================================================================== --- | df バインド済の plot。 描画関数 (saveSVGBound 等、 backend package 側) が--- 消費する束。------ 'bpDiagnostics' は '(|>>)' バインド時の検証結果を **値として**運ぶ--- (proposal spec-2 §3.3 / Phase 14 検証案1)。 '(|>>)' 自身は例外を投げない--- 純関数なので、 @let p = df |>> spec@ を list に詰める・テストで比較するが--- 成り立つ (= 「plot は値」)。 Error severity の報告は描画関数が実行時に行う。+-- | [日本語]: df バインド済の plot。 描画関数 (@saveSVGBound@ 等、 backend+-- package 側) が消費する束。 'bpDiagnostics' は '(|>>)' バインド時の検証結果を+-- __値として__ 運ぶ (proposal spec-2 §3.3)。 '(|>>)' 自身は例外を投げない+-- 純関数なので、 @let p = df |>> spec@ を list に詰める・テストで比較するが+-- 成り立つ (= 「plot は値」)。 Error severity の報告は描画関数が実行時に行う。+-- [English]: A plot with its dataframe already bound — the bundle consumed by+-- the rendering functions (@saveSVGBound@ and friends, which live in the+-- backend packages). 'bpDiagnostics' carries the validation result produced+-- at '(|>>)' bind time __as a value__. '(|>>)' itself is a pure function that+-- never throws, so @let p = df |>> spec@ can be put in a list or compared in+-- tests (that is, "a plot is a value"). Reporting of error-severity+-- diagnostics is left to the rendering function at run time. data BoundPlot = BoundPlot { bpResolver :: Resolver , bpSpec :: VisualSpec , bpDiagnostics :: [PlotDiagnostic] } --- | df を spec にバインド。 **純関数** (例外を投げない)。------ 演算子が @|>@ でなく @|>>@ なのは、 Hackage @dataframe@ が @|>@ を public--- export しており衝突するため (proposal spec-2 / Phase 14 計画書 §設計判断)。--- 'infixl' 1 で @<>@ (infixr 6) より弱く、 @df |>> (layer a <> layer b)@ を--- カッコ無しで書ける。------ 検証 (A5): バインド時に 'validatePlotWith' で spec の 'ColByName' を df の--- 'columnNames' と突合し、 結果を 'bpDiagnostics' に **値として**格納する--- (存在しない列 → 'ColumnNotFound' + 編集距離 suggestion、 型不一致 →--- 'ColumnTypeMismatch'、 必須 aesthetic 欠落、 空 plot を検出)。 空 df--- (@nrows == 0@) は専用 error kind が無いため 'PlotInfo' で残す (lenient)。--- 例外は一切投げない。 Error の報告は描画関数 ('saveSVGBound') が実行時に行う。--- Phase 24 A6: spec 型で束の型を選ぶ (2D = 'BoundPlot'、 3D = BoundPlot3D)。--- 2D の意味論は従来と同一 (instance が旧実装そのもの)。 3D instance は--- hgg-3d 側 (型の定義 package = 非 orphan)。+-- | [日本語]: df を spec にバインド。 __純関数__ (例外を投げない)。+-- 演算子が @|>@ でなく @|>>@ なのは、 Hackage @dataframe@ が @|>@ を public+-- export しており衝突するため。 @infixl 1@ で @<>@ (@infixr 6@) より弱く、+-- @df |>> (layer a <> layer b)@ をカッコ無しで書ける。+-- バインド時に 'validatePlotWith' で spec の列参照を df の 'columnNames' と+-- 突合し、 結果を 'bpDiagnostics' に __値として__ 格納する (存在しない列 →+-- 編集距離つき suggestion、 型不一致、 必須 aesthetic 欠落、 空 plot を検出)。+-- 空 df (@nrows == 0@) は専用 error kind が無いため 'PlotInfo' で残す+-- (lenient)。 例外は一切投げない。 束の型は spec 型で選ぶ+-- (2D = 'BoundPlot'、 3D = @BoundPlot3D@。 3D instance は hgg-3d 側 =+-- 型の定義 package なので非 orphan)。+-- [English]: Binds a dataframe to a spec. __Pure__ — it never throws. The+-- operator is @|>>@ rather than @|>@ because Hackage @dataframe@ exports+-- @|>@ publicly and the two would clash. At @infixl 1@ it binds more weakly+-- than @<>@ (@infixr 6@), so @df |>> (layer a <> layer b)@ needs no+-- parentheses. At bind time 'validatePlotWith' checks the spec's column+-- references against the dataframe's 'columnNames' and stores the result in+-- 'bpDiagnostics' __as a value__ (missing columns with an edit-distance+-- suggestion, type mismatches, missing required aesthetics and empty plots+-- are all detected). An empty dataframe (@nrows == 0@) is reported as+-- 'PlotInfo' because there is no dedicated error kind for it (lenient).+-- The bound type is selected by the spec type (2D gives 'BoundPlot', 3D+-- gives @BoundPlot3D@, whose instance lives in hgg-3d — the package+-- that defines the type, so it is not an orphan). class BindableSpec spec where type BoundOf spec bindData :: PlotData df => df -> spec -> BoundOf spec@@ -129,17 +170,24 @@ where r = toResolver df --- | 空 df の共通診断 (2D/3D instance で共有)。+-- | [日本語]: 空 df の共通診断 (2D/3D instance で共有)。+-- [English]: The shared diagnostic for an empty dataframe (used by both the+-- 2D and 3D instances). emptyDfDiagnostics :: PlotData df => df -> [PlotDiagnostic] emptyDfDiagnostics df | nrows df == 0 = [PlotInfo "DataFrame が空です (nrows == 0)。 描画は空になります。"] | otherwise = [] +-- | [日本語]: df を spec にバインドする演算子 ('bindData' の別名)。+-- [English]: The operator form of 'bindData', binding a dataframe to a spec. (|>>) :: (PlotData df, BindableSpec spec) => df -> spec -> BoundOf spec (|>>) = bindData infixl 1 |>> --- | 検証を逃がす raw 経路。 'BoundPlot' から (Resolver, VisualSpec) を取り出し、--- 既存の @saveSVGWith@ / @renderSVGWith@ に直接渡せる。+-- | [日本語]: 検証を逃がす raw 経路。 'BoundPlot' から @(Resolver, VisualSpec)@ を+-- 取り出し、 既存の @saveSVGWith@ / @renderSVGWith@ に直接渡せる。+-- [English]: The raw escape hatch that bypasses validation. Extracts+-- @(Resolver, VisualSpec)@ from a 'BoundPlot' so that it can be passed+-- straight to the existing @saveSVGWith@ / @renderSVGWith@. unBound :: BoundPlot -> (Resolver, VisualSpec) unBound (BoundPlot r s _) = (r, s)