Haskell拡張で暗黙的なデータ変換を行って比較する型クラスを作ることは可能ですが, 少なくとも私の素朴な実装は実用的ではない
Haskell, Maybe a型とa型を比較できるようEqとかOrdが定義されてないのはなぜだろう。
— Ryou Ezoe (@EzoeRyou) 2018年1月12日
Just x == y = x == y
x == Just y = x == y
みたいな。
Eq a bというふうに型を2つとる設計にしなかったのはなぜだろう。NumとIntとFloatみたいに共通の型を作れってことなんだろうか。
— Ryou Ezoe (@EzoeRyou) 2018年1月12日
そういうclassの作成は確か初期のHaskellだとそもそもできなかったはずですhttps://t.co/h18PSF0vpv
— エヌユル (@ncaq) 2018年1月12日
あと,現実的な対処として
— エヌユル (@ncaq) 2018年1月12日
Prelude> Just 1 == pure 1
True
Prelude> Right 1 == pure 1
True
のようなことが可能でなので別に分解が統一されてなくても問題がないです
Haskell使いはコンテナ(?)に包む方式を共通して作りはしますが,分解はパターンを網羅できなくて危険なのでやりたがらないですね
Haskellの設計当初は型クラスの型引数は1つしか取れなかったはずですが, 今はMulti-parameter type class - HaskellWikiがあるから可能ですね. 実際実装できるか試してみましょう.
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module ImplicitEq where
class ImplicitEq a b where
(~~) :: a -> b -> Bool
instance Eq a => ImplicitEq a a where
(~~) = (==)
instance Eq a => ImplicitEq (Maybe a) a where
Nothing ~~ _ = False
Just l ~~ r = l == r
instance Eq a => ImplicitEq a (Maybe a) where
_ ~~ Nothing = False
l ~~ Just r = l == r
instance Eq b => ImplicitEq (Either a b) b where
Left _ ~~ _ = False
Right l ~~ r = l == r
instance Eq b => ImplicitEq b (Either a b) where
_ ~~ Left _ = False
l ~~ Right r = l == r
型クラスは実装できました.
そしてMaybe
, Either
に対するinstance
も実装できました.
これはおそらく期待通りに動きます.
しかし, 実用的かというとそうではないです.
例えばghciだとJust 1 ~~ 1
は型推論が出来なくてエラーになるんですね.
使うべきインスタンスの決定が出来ない.
Just 1 ~~ (1 :: Int)
でもダメです.
Just (1 :: Int) ~~ (1 :: Int)
なら完全に型が定まるので可能です.
Just "foo" ~~ "foo"
は素ではOKですが,
:set -XOverloadedStrings
するとやはりダメですね.
私が実はHaskellの型システムをあまり理解していないことが露呈してきましたね.
しかし, 実際のコードではその前に引数や関数の返り値で型が決定されているから実用的なのではないか?
λ> let f = Just 1 :: Maybe Int
λ> f
Just 1
λ> :i f
f :: Maybe Int -- Defined at <interactive>:94:5
λ> f ~~ 1
しかしこれが通らないということはやはりダメですね.
実用的ではない.
f ~~ (1 :: Int)
なら通りますが.
まあ,
Haskellにはpure
があるのでこんなものは元より不要なので,
実用上の問題はありません.
型を分解するより,
構築しましょう.