top page > computer > haskell > extensions > quasiquotation > lisp > data.html
更新日:
文責: 重城良国

Lispっぽく表記: 代数的データ型の定義

どのような表記にするか

Haskellでは以下のような形になる。

data Foo a = Bar | Baz Int | FooBar 

Lispっぽい表記では以下のようにすれば良い。

(data (Foo a) Bar (Baz Int) FooBar)

deriving節

代数的データ型の定義にはderiving節が付く場合もある。この場合の表記も考えてみよう。以下のようにすれば良いと思う。

(deriving (data (Foo a) Bar (Baz Int) FooBar) Show Eq)

Lexer.hs

Lexer.hs

data Token =
	...
	| Comma | Arrow | Type | Data | Deriving
	...
...
lexer ('d' : 'a' : 't' : 'a' : cs@(c : _))
	| not $ isAlphaNum c = Data : lexer cs
lexer ('d' : 'e' : 'r' : 'i' : 'v' : 'i' : 'n' : 'g' :
		cs@(c : _))
	| not $ isAlphaNum c = Deriving : lexer cs
...

Parser.hs: 代数的データ型の定義

Parser.hs

parseConstructor

値構築子のパース。

parseConstructor :: [Token] -> (ConQ, [Token])
parseConstructor (Con v : ts) = (normalC (mkName v) [], ts)
parseConstructor (OP : Con v : ts)
	| Just (tps, ts') <- parseTypeList ts =
		(normalC (mkName v) $
			map (strictType notStrict) tps, ts')
parseConstructor ts = error $
	"parseConstructor: parse error: " ++ show ts

parseConstructors

値構築子の並びのパース。

parseConstructors :: [Token] -> ([ConQ], [Token])
parseConstructors (CP : ts) = ([], ts)
parseConstructors ts = let (c, ts') = parseConstructor ts in
	first (c :) $ parseConstructors ts'

parseVars

変数の並びのパース。

parseVars :: [Token] -> ([Name], [Token])
parseVars (CP : ts) = ([], ts)
parseVars (Var v : ts) = first (mkName v :) $ parseVars ts
parseVars ts = error $ "parseVars: parse error: " ++ show ts

parseDataHead

代数的データ型の定義の頭の部分のパース。

parseDataHead :: [Token] -> ((Name, [TyVarBndr]), [Token])
parseDataHead (Con v : ts) = ((mkName v, []), ts)
parseDataHead (OP : Con v : ts) =
	first ((mkName v ,) . map PlainTV) $ parseVars ts
parseDataHead ts = error $
	"parseDataHead: parse error: " ++ show ts

parseDataDec

代数的データ型の定義のパース。

parseDataDec :: [Token] -> ([Name] -> DecQ, [Token])
parseDataDec ts = let ((n, vbs), ts') = parseDataHead ts in
	first (dataD (cxt []) n vbs) $ parseConstructors ts'

parseDataDecを使う

parseDataDecを使ってparseDecが代数的データ型の定義をパースできるようにする。

parseDec (OP : Data : ts) = let
	(dd, ts') = parseDataDec ts in
	(:) <$> dd [] <*> parseDec ts'

Parser.hs: deriving節の追加

parseCons

定数の並びをパースする。

parseCons :: [Token] -> ([Name], [Token])
parseCons (CP : ts) = ([], ts)
parseCons (Con v : ts) = first (mkName v :) $ parseCons ts
parseCons ts = error $ "parseCons: parse error: " ++ show ts

parseDecにderiving節を追加

deriving節の付いた代数的データ型の定義をパースできるようにする。

parseDec (OP : Deriving : OP : Data : ts) = let
	(dd, ts') = parseDataDec ts
	(cs, ts'') = parseCons ts' in
	(:) <$> dd cs <*> parseDec ts''

試してみる

useLispLike.hs

[lisp|

(data (Foo a) Bar (Baz a) FooBar)

(deriving (data (Hoge a) Fuga (Piyo a) HogeHoge) Show Eq)

...

|]
% ghci useLispLike.hs
*Main> :info Foo
data Foo a = Bar | Baz a | FooBar
*Main> :info Hoge
data Hoge a = Fuga | Piyo a | HogeHoge
instance Eq a => Eq (Hoge a)
instance Show a => Show (Hoge a)

「型注釈」へもどる 「型変数の文脈」へ

正当なCSSです! HTML5 Powered with CSS3 / styling, and Semantics