短い検索では答えを見つけることができなかったので、私はその存在に疑問を抱き始めました。質問は簡単です。次のようなポリモーフィック関数を作成したいと思います。
f :: String -> String
f s = show (length s)
f :: Int -> String
f i = show i
データ型ごとに異なる方法で定義された関数を意味します。出来ますか?はいの場合、どのように?
短い検索では答えを見つけることができなかったので、私はその存在に疑問を抱き始めました。質問は簡単です。次のようなポリモーフィック関数を作成したいと思います。
f :: String -> String
f s = show (length s)
f :: Int -> String
f i = show i
データ型ごとに異なる方法で定義された関数を意味します。出来ますか?はいの場合、どのように?
Haskellには2つのポリモーフィズムのフレーバーがあります。
1つ目は最も一般的なものです。関数は、その型パラメーターの少なくとも1つで、すべての型に対して均一に動作する場合、パラメトリックに多形です。
たとえば、関数length
は多態性です。リストに格納されているタイプに関係なく、リストの長さを返します。
length :: [a] -> Int
多型は小文字の型変数で示されます。
これで、特定のタイプのセットに対して必要なカスタム動作がある場合は、ポリモーフィズム(「アドホック」とも呼ばれます)が制限されています。Haskellでは、このために型クラスを使用します。
このクラスは、一連のタイプで使用できる関数を宣言します。
class FunnyShow a where
funnyshow :: a -> String
次に、気になるタイプごとにインスタンスを定義できます。
instance FunnyShow Int where
funnyshow i = show (i+1)
そして多分:
instance FunnyShow [Char] where
funnyshow s = show (show s)
型族を使用して同様のことを実現する方法は次のとおりです。
同じ戻り型がある場合は、型族を使用せずに、Donが提案するように型クラスのみを使用して動作を実現できます。
ただし、インスタンスごとに異なる戻り型など、より複雑なアドホック多相性をサポートする場合は、型ファミリーを使用することをお勧めします。
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE TypeFamilies #-}
class F a where
type Out a :: *
f :: a -> Out a
instance F String where
type Out String = String
f = show . length
instance F Int where
type Out Int = String
f = show
instance F Float where
type Out Float = Float
f = id
ghciで
*Main> f (2::Int)
"2"
*Main> f "Hello"
"5"
*Main> f (2::Float)
2.0