質問

でHaskellは、それが二つの異なる(類似であるが)データ型を受け入れることができる署名で関数を作成し、

に渡された種類に応じて異なる動作させることが可能です

の例では、私の質問をより明確にすることがあります。私はmyFunctionという名前の関数を持っている、とMyTypeAMyTypeBという名前の2つのタイプの場合、私はmyFunctionを定義することができ、それが唯一のその最初のパラメータとして型MyTypeAMyTypeBのデータを受け入れることができるように?

type MyTypeA = (Int, Int, Char, Char)
type MyTypeB = ([Int], [Char])

myFunction :: MyTypeA_or_MyTypeB -> Char
myFunction constrainedToTypeA = something
myFunction constrainedToTypeB = somethingElse

OOP言語では、あなたは私がそうのように達成しようとしているものを書くことができます:

public abstract class ConstrainedType {
}

public class MyTypeA extends ConstrainedType {
    ...various members...
}

public class MyTypeB extends ConstrainedType {
    ...various members...
}

...

public Char myFunction(ConstrainedType a) {
    if (a TypeOf MyTypeA) {
        return doStuffA();
    }
    else if (a TypeOf MyTypeB) {
        return doStuffB();
    }
}

私は代数的データ型について読んでてきたと私はHaskellのの種類を定義する必要があると思うが、私は確信してどのようにそれが一つのタイプを保存することができるようにそれを定義については行かないんですか別の、そしてまた、私は私自身の機能でそれを使用する方法について説明します。

役に立ちましたか?

解決

はい、あなたは正しい、あなたは代数的データ型を探しています。彼らの偉大なチュートリアルでは、あなたを学びHaskellのでありますのます。

は記録のために、OOPから抽象クラスの概念は、実際にHaskellのに三つの異なる翻訳を持っている、とのADTはただ一つです。ここでは技術の概要です。

代数的データ型

は代数的データ型は、サブクラスが知られている抽象クラスのパターンを符号化、および機能は、オブジェクトがダウンキャスティングによってのメンバーである特定のどのインスタンスチェックここ。

abstract class IntBox { }

class Empty : IntBox { }

class Full : IntBox {
    int inside;
    Full(int inside) { this.inside = inside; }
}

int Get(IntBox a) {
    if (a is Empty) { return 0; }
    if (a is Full)  { return ((Full)a).inside; }
    error("IntBox not of expected type");
}

に変換ます:

data IntBox = Empty | Full Int

get :: IntBox -> Int
get Empty = 0
get (Full x) = x

機能の録音

上記Get機能はこのスタイルで表現できることがないように、

このスタイルは、ダウンキャストはできません。そこでここでは完全に異なるものです。

abstract class Animal { 
    abstract string CatchPhrase();
    virtual void Speak() { print(CatchPhrase()); }
}

class Cat : Animal {
    override string CatchPhrase() { return "Meow"; }
}

class Dog : Animal {
    override string CatchPhrase() { return "Woof"; }
    override void Speak() { print("Rowwrlrw"); }
}

Haskellではその翻訳は型に型をマップしません。 Animalは唯一のタイプであり、かつDogCatは、そのコンストラクタ関数に離れて押しつぶされます:

data Animal = Animal {
    catchPhrase :: String,
    speak       :: IO ()
}

protoAnimal :: Animal
protoAnimal = Animal {
    speak = putStrLn (catchPhrase protoAnimal)
}

cat :: Animal
cat = protoAnimal { catchPhrase = "Meow" }

dog :: Animal
dog = protoAnimal { catchPhrase = "Woof", speak = putStrLn "Rowwrlrw" }

この基本的な概念のいくつかの異なる順列があります。不変の抽象型は、メソッドは、レコードのフィールドですレコードタイプがあるということです。

EDIT:上記のコードのバグを含む、このアプローチの機微のいくつかのコメントでは良い議論が、あります。

型クラス

これはOOのアイデアの私の最も好きなエンコーディングです。それは身近な単語を使用し、型に型をマップするので、それはOOプログラマに快適です。しかし、機能の記録上記のアプローチは、物事が複雑になるときと作業が容易になる傾向がある。

私は再び動物の例をエンコードします。

class Animal a where
    catchPhrase :: a -> String
    speak       :: a -> IO ()

    speak a = putStrLn (catchPhrase a)

data Cat = Cat 
instance Animal Cat where
    catchPhrase Cat = "Meow"

data Dog = Dog
instance Animal Dog where
    catchPhrase Dog = "Woof"
    speak Dog = putStrLn "Rowwrlrw"

このルックスの素敵な、それはしないのですか?あなたはそれがOOのように見えるにもかかわらず、それは本当にOOのような作業をしないことを認識したときに困難が来ます。あなたは動物のリストを持っている場合がありますが、あなたが今できる最善のはAnimal a => [a]、均質な動物のリスト、例えばあります。唯一の猫や犬だけのリスト。そして、あなたは、このラッパー型にする必要があります:

{-# LANGUAGE ExistentialQuantification #-}

data AnyAnimal = forall a. Animal a => AnyAnimal a
instance Animal AnyAnimal where
    catchPhrase (AnyAnimal a) = catchPhrase a
    speak (AnyAnimal a) = speak a

そして[AnyAnimal]あなたは動物のあなたのリストについては、何をしたいです。しかし、それは第二の例ではAnyAnimalレコードと自身に関する正確の同じ情報Animalが公開にをことが判明し、私たちは遠回しにそれについて行ってきました。したがって、なぜ私は型クラスは、オブジェクト指向の非常に良いエンコーディングであることを考慮していない。

これ

のあまりにも多くの情報!

の今週の版を締結

他のヒント

それはあなたが型クラスの上に読みたいかもしれないように聞こえます。

の型クラスのを使用してこの例を考えてみましょう。

するためにMVC MultiParamTypeClasses tState:3種類(ノートtAction)に基づく「抽象クラス」tReactionのような -

私たちは、C ++を定義します アクションが状態に適用した場合、あなたは新しい状態と反応を得る(キー機能tState -> tAction -> (tState, tReaction)を定義します。

型クラスではあり 3「C ++抽象」機能、およびいくつかのより多くの「抽象的」なものに定義されています。必要なときとinstance MVC「抽象的」機能が定義されます。

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, NoMonomorphismRestriction #-}


-- -------------------------------------------------------------------------------

class MVC tState tAction tReaction | tState -> tAction tReaction where
      changeState :: tState -> tAction -> tState       -- get a new state given the current state and an action ("abstract")
      whatReaction :: tState -> tReaction              -- get the reaction given a new state ("abstract")
      view :: (tState, tReaction) -> IO ()             -- show a state and reaction pair ("abstract")

      -- get a new state and a reaction given an state and an action (defined using previous functions)
      runModel :: tState -> tAction -> (tState, tReaction) 
      runModel s a = let
                                ns = (changeState s a) 
                                r = (whatReaction ns) 
                  in (ns, r)

      -- get a new state given the current state and an action, calling 'view' in the middle (defined using previous functions)
      run :: tState -> tAction -> IO tState
      run s a = do
                        let (s', r) = runModel s a
                        view (s', r)
                        return s'

      -- get a new state given the current state and a function 'getAction' that provides actions from "the user" (defined using previous functions)
      control :: tState -> IO (Maybe tAction) -> IO tState
      control s getAction = do
              ma <- getAction
              case ma of
                   Nothing -> return s
                   Just a -> do
                              ns <- run s a
                              control ns getAction


-- -------------------------------------------------------------------------------

-- concrete instance for MVC, where
-- tState=Int tAction=Char ('u' 'd') tReaction=Char ('z' 'p' 'n')
-- Define here the "abstract" functions
instance MVC Int Char Char where
         changeState i c 
                     | c == 'u' = i+1 -- up: add 1 to state
                     | c == 'd' = i-1 -- down: add -1 to state
                     | otherwise = i -- no change in state

         whatReaction i
                      | i == 0 = 'z' -- reaction is zero if state is 0
                      | i < 0 = 'n' -- reaction is negative if state < 0                     
                      | otherwise = 'p' -- reaction is positive if state > 0

         view (s, r) = do
                  putStrLn $ "view: state=" ++ (show s) ++ " reaction=" ++ (show r) ++ "\n"

--

-- define here the function "asking the user"
getAChar :: IO (Maybe Char) -- return (Just a char) or Nothing when 'x' (exit) is typed
getAChar = do
         putStrLn "?"
         str <- getLine
         putStrLn ""
         let c = str !! 0
         case c of
              'x' -> return Nothing
              _ -> return (Just c)


-- --------------------------------------------------------------------------------------------
-- --------------------------------------------------------------------------------------------

-- call 'control' giving the initial state and the "input from the user" function 
finalState = control 0 getAChar :: IO Int

-- 

main = do
     s <- finalState
     print s
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top