2011-07-09 15 views
16

私はHaskell多型に関する質問があります。Haskellは多重定義の多型をどのように扱いますか?

私が学んだように、多型の2種類があります。

  1. パラメトリック:あなたが入力タイプを指定しないでください。

    例:

    functionName :: [a] -> a 
    
  2. オーバーロード:命令型プログラミングのように、すなわち、同じ機能に異なる引数を渡します。

私の問題は、次のとおりです。どのようにHaskellはをオーバーロードを処理しますか?

答えて

40

Haskellでのオーバーロードは、型クラスを使用して行われます。たとえば、あなたがIntを返す関数fooをオーバーロードしたいとしましょう:

class Fooable a where 
    foo :: a -> Int 

instance Fooable Int where 
    foo = id 

instance Fooable Bool where 
    foo _ = 42 

はしかし、彼らはほとんどの言語で見られるオーバーロードメカニズムよりも強力です。たとえば、あなたが戻り値の型にオーバーロードすることができます。より多くの例については

class Barable a where 
    bar :: Int -> a 

instance Barable Int where 
    bar x = x + 3 

instance Barable Bool where 
    bar x = x < 10 

、Haskellではpredefined type classesを見ています。

+1

@Zach:オーバーロードは確かに多型の形と考えられています。参考:ウィキペディアの[Ad-hoc polymorphism](http://en.wikipedia.org/wiki/Ad-hoc_polymorphism)あなたの質問の第2部分であなたが何を指しているのか分かりません。明確にしていただけますか? – hammar

+0

私は 'class Barable'を使用しようとしています。整数 '5'に等しい変数xがあるとします。 'bar x'は動作しませんでした。仕事の場合には、「8」か「false」を返すでしょうか?あいまいな例です。 – Jaider

+1

@Jaider:型は、通常、型の注釈を使用して強制することはできますが、型が使用されているコンテキストから推論されます。 'bar 5 :: Int'または' bar 5 :: Bool'を試してください。 – hammar

3

関数の型シグネチャを元の型クラスに指定し、その関数の複数インスタンスを作成します。したがって、hammarの例ではと多相であり、それぞれのインスタンスで指定された型(例:Fooable Bool)がaの型(この場合はaはBool)であると考えることができます。したがって、関数fooをBool値で呼び出すと、Bool値のFooableのインスタンスが呼び出されます。

ところで、あなたはタイプクラスに複数の関数を置くことができ、定義された他の関数に関しても関数を定義することができます。

class Eq a where 
    (==), (/=) :: a -> a -> Bool 

    x /= y = not (x == y) 
    x == y = not (x /= y) 

それはここでは明らかではないかもしれませんが、あなたは、式のインスタンスを定義した場合、あなただけの彼らはお互いの観点から定義されているので、両方とも、==または/ =をない定義する必要があります。

4
似ていますが、異なる機能を提供する複数の関数に同じ名前を使用しての手段に過負荷をいくつかの言語で

、ので、あなたは、あなたが取得しているDuplicate type signatureエラーを与えるだろう

split :: String -> [String]      -- splits on whitespace 
split :: Char -> String -> [String]    -- splits on the given character 
split :: [Char] -> String -> [String]   -- splits on any of the given characters 
split :: (Char -> Bool) -> String -> [String] -- splits using a function that tells you when 

てみてください。

Haskellは過負荷に、このタイプのを行いませんし、Haskellのプログラマは、これらの異なる名前を与えるだろう:Haskellは私はあなたがについて尋ねていると思うの過負荷の種類を許可していません

words :: String -> [String]      -- splits on whitespace 
splitOn :: Char -> String -> [String]    -- splits on the given character 
splitsOn :: [Char] -> String -> [String]   -- splits on any of the given characters 
splitWith :: (Char -> Bool) -> String -> [String] -- splits using a function that tells you when 

理由をそれが真にあなたがそれなしではできない何かをすることを許さないということであり、より高度な種類の過負荷を行うことはほとんど不可能になります。ハスケルのオーバーロードは確かに非常に強力なツールです。タイプクラスとコンストラクタークラスについて調べてみましょう。 String = [Char]、およびHaskellのプログラマはコードの再利用を愛しているので

実際、彼らは書くことがはるかに可能性が高いと思います。ここでは

words :: String -> [String]    -- splits on whitespace 
splitOn :: Eq a => a -> [a] -> [[a]]  -- splits on the given item 
splitsOn :: Eq a => [a] -> [a] -> [[a]] -- splits on any of the given items 
splitWith :: (a -> Bool) -> [a] -> [[a]] -- splits using a function that tells you when 

Eq aは、どこHaskellは許可しないのオーバーロードのようなものの一例ですsplitOnは、項目が等しいかどうか比較することができる限り、リストを分割することができます(つまり、Haskellでは独自の独自性を定義できます)。これを使用して文字列を分割することもできますし、例えば文字列のリストを分割することもできますが、2つの関数が同じかどうかをチェックすることができないため、関数のリストを分割することはできません。 splitWithは、他のほとんどのデータと同じように関数を扱えるようにするHaskellの例です。引数として渡すことができます!

[注1:wordssplitWithが若干異なるtypesignatureで図書館であり、標準機能である。]
[注2:あなたが実際にこれらの関数を作成したい場合は、ここに方法は次のとおりです。

splitWith isSplitter list = case dropWhile isSplitter list of 
    [] -> [] 
    thisbit -> firstchunk : splitWith isSplitter therest 
    where (firstchunk, therest) = break isSplitter thisbit 

-- words = splitWith isSpace   -- not needed, standard function from the Prelude 
splitOn c = splitWith (== c)   -- notice I passed == in an argument! 
splitsOn chars = splitWith (`elem` chars) 
あなたが似何かを行うことができますが、

]

1

は基本的にオーバーライドは、Haskellのではかなり異なっています。

I.選択された答えとしてクラスを使用します。

class YesNo a where 
    yesno :: a -> Bool 

あなたは、インスタンスを使用して、それを実装する必要があり、その後、あなたはこのようにそれを使用することができます:

> yesno $ length [] 
False 
> yesno "haha" 
True 
> yesno "" 
False 
> yesno $ Just 0 
True 
> yesno True 
True 
ghci> yesno EmptyTree 
False 
> yesno [] 
False 
> yesno [0,0,0] 
True 

http://learnyouahaskell.com/making-our-own-types-and-typeclasses

II。以下のような、型コンストラクタのパターンマッチングを使用します。

data Shape = Circle Float Float Float | Rectangle Float Float Float Float 
surface :: Shape -> Float 
surface (Circle _ _ r) = pi * r^2 
surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1) 

、その後、あなたは、このようにそれらを使用することができます。

> surface $ Circle 10 20 10 
314.15927 
> surface $ Rectangle 0 0 100 100 
10000.0 

http://learnyouahaskell.com/making-our-own-types-and-typeclasses

III。ここで

http://learnyouahaskell.com/modules

0

に名前を付け、あなたは

  • AD-を組み合わせた簡単な例を持ってsepatateモジュールに「oveloaded」機能を持っていますが、インポート名または修飾されたインポートの前に付ける必要があります。 (オーバーロード):異なるタイプの異なる動作(Haskellタイプのクラスを使用)で同じ機能です(Haskellタイプのクラスを使用)。

  • パラメトリック多形:異なるタイプの同じ動作(パラメータ化されたタイプの関数を使用)。 原則として、型は関係ありませんが、型クラスを使用して 許容型を制限しています)。

コード:

import Data.Char 

class MyTypeFamily t where 
     f1 :: t -> Int 
     f2 :: t -> Int 

instance MyTypeFamily Int where 
     f1 x = x*x 
     f2 x = x+x 

instance MyTypeFamily Char where 
     f1 x = (ord x) * (ord x) 
     f2 x = (ord x) + (ord x) 

instance MyTypeFamily Bool where 
     f1 x 
      | x = 10 
      | otherwise = 10 
     f2 x 
      | x = 100 
      | otherwise = -100 

-- ............................................................... 
-- using f1, f2 as "overloaded" functions ("ad-hoc polymorphism) 
-- (the algorithm for f1, f2 is chosen depending on their type) 
-- 
-- using fun as polymorphic (parametric polymorphic function) 
-- the algorithm of fun is always the same but it works on 
-- different types 
fun :: (MyTypeFamily t) => t -> Int 
fun x = (f1 x) + (f2 x) 

-- ............................................................... 
-- ............................................................... 
main = do 
     print $ fun 'J' 
     print $ fun True 
     print $ fun False 
     print $ fun (8 :: Int) 
関連する問題