• 作成:

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があるのでこんなものは元より不要なので, 実用上の問題はありません. 型を分解するより, 構築しましょう.