前回までのあらすじ
Haskellで文字列っぽい型同士を結合する演算子+++
が欲しい。左辺、右辺、返り値の関係は下表のようになって欲しい。また、 foo :: String +++ bar :: ByteString
のような型注釈は不要なようにしたい
左辺 \ 右辺 | 文字列リテラル | String | Lazy.Text | Text | Lazy.ByteString | ByteString |
---|---|---|---|---|---|---|
文字列リテラル | String | String | Lazy.Text | Text | Lazy.ByteString | ByteString |
String | String | Lazy.Text | Text | Lazy.ByteString | ByteString | |
Lazy.Text | Lazy.Text | Text | Lazy.ByteString | ByteString | ||
Text | Text | Lazy.ByteString | ByteString | |||
Lazy.ByteString | Lazy.ByteString | ByteString | ||||
ByteString | ByteString |
そういう型クラスFlexibleAppendable
を作ってみるが、Haskell力が足らずにコンパイルが通るものが作れなかった。
コンパイルが通って動作するところまで到達したバージョン
- コメント欄で @hiratara さんという方に
{-# OVERRAPPABLE #-}
プラグマをおしえてもらったので使ってみた -
{-# OverloadedStrings #-}
な文字列リテラルから中身の文字列を取り出すのに、unsafeCoerce
を使ってみた
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE UndecidableInstances #-}
import qualified Data.ByteString.Char8 as CB
import Data.Monoid
import Data.String
import Unsafe.Coerce
class FlexibleAppendable l r a where
(+++) :: l -> r -> a
instance {-# OVERLAPPABLE #-} (IsString l) => FlexibleAppendable l CB.ByteString CB.ByteString where
(+++) l r = fromString (unsafeCoerce l :: String) <> r
instance {-# OVERLAPPABLE #-} (IsString l, IsString r) => FlexibleAppendable l r String where
(+++) l r = fromString $ (unsafeCoerce l :: String) <> (unsafeCoerce r :: String)
-- 以下似たようなものがえんえん続く
動かしてみる
main :: IO ()
main = do
putStrLn $ "Hello, " +++ "World"
CB.putStrLn $ "Hello, " +++ CB.pack "World"
実行結果
Hello, World
Hello, World
動いた。すごい!
ただ、この実装の場合こちらが想定している型以外の独自のIsStringインスタンスが渡された際にruntime corruptionが発生することがあります。こちらが把握していないデータ構造に対して強制キャストをするためです。Unsafe.Coerceの解説に書いてありました。やばそうなので改善したい機運が凄いです。
進展があったら「その3」を投稿します。進展ありました→ http://qiita.com/saturday06/items/72c98df4e3d1481962b1