LoginSignup
3
0

More than 5 years have passed since last update.

QuasiQuoteでopen-unionを書きやすくするライブラリを作ってみました

Last updated at Posted at 2018-02-20

はじめに

前回の記事では、open-unionを使ってみました。

そこで今回はopen-unionをより書きやすくするために、ちょっとしたライブラリを作ってみたので、紹介します。

普通のopen-union

そのままの、open-unionでは、関数を書くときは以下のようにして、型ごとにマッチさせます。きっと型安全でとてもいいのですが、見た目が、通常のパターンマッチングらしくないですよね。

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}

import Data.OpenUnion


-- 普通のopen-union
showMyUnion :: Union '[Char, Int, [()]] -> String
showMyUnion
    =  (\(c :: Char)   -> "char: " ++ show c)
    @> (\(i :: Int)    -> "int: " ++ show i)
    @> (\(l :: [()])   -> "list length: " ++ show (length l))
    @> (\(s :: String) -> "string: " ++ s)
    @> typesExhausted

open-union-sugarを使ったとき

open-union-sugarが今回紹介するライブラリです)
以下のように[ptn| ... ]の中に書けば、普通のHaskellのパターンマッチのように書くことができるようになります。

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}

import Data.OpenUnion
import Data.OpenUnion.Sugar

type UnitList = [()]

showMyUnion :: Union '[Char, Int, String, UnitList] -> String
[ptn|
showMyUnion (c :: Char)     = "char: " ++ show c
showMyUnion (i :: Int)      = "int: " ++ show i
showMyUnion (s :: String)   = "string: " ++ s
showMyUnion (l :: UnitList) = "list length: " ++ show (length l)
|]

ヘテロリスト

他にもヘテロリストを簡単につくれる[hlist| ... |]などがあります。

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE QuasiQuotes #-}


import Data.OpenUnion
import Data.OpenUnion.Sugar

main :: IO ()
main = do
    let hlist1 :: [Union '[Char, Bool, String]]
        hlist1 = [hlist|
                    'a'
                  , True
                  , "apple"
                  , 'z'
                  , False
                  , "orange"
                 |]
    print hlist1
    -- => [Union ('a' :: Char),Union (True :: Bool),Union ("apple" :: [Char]),Union ('z' :: Char),Union (False :: Bool),Union ("orange" :: [Char])]

open-union-sugarの導入法

Stackをお使いの方は、stack.yamlextra-depsに以下を追記すればOKです(commit:のところは、その時の最新版にコミットID変更したり、定義変更してください)。

...
extra-deps:
- git: git@github.com:nwtgck/open-union-sugar-haskell.git
  commit: 24ad5c35054dc511308bb5186cf17784042c499a
...

実装について

主なQuasiQuoteの実装は以下の場所にあります。
src/Data/OpenUnion/Sugar.hs

[ptn | ... ]

[ptn | ... ]の構文解析はhaskell-src-metaパッケージのLanguage.Haskell.Meta.ParseモジュールparseDecsを使ってます。

以下のコードは意味解析のときにはエラーですが、構文解析のときはエラーにならないようなので、思ったように構文木をparseDecsを使って取得することができました。

showMyUnion (c :: Char)     = "char: " ++ show c
showMyUnion (i :: Int)      = "int: " ++ show i
showMyUnion (s :: String)   = "string: " ++ s
showMyUnion (l :: UnitList) = "list length: " ++ show (length l)

[hlist | ... ]

[hlist | ... ]の構文解析にはparseExpが使われていて、これも[ptn| ... |]と同様に、以下のリストは意味解析的にはエラーですが、構文解析にはOKなので、思ったように構文木が手に入りました。

['a' , True , "apple", 'z', False, "orange"]
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0