2012-01-13 2 views
4

Я пробовал написать простую функцию, которая берет любой тип (возможно, параметризуемый двумя разными типами) и делает одну вещь, если она получает Left и другую вещь, если она становится правой. следующий код,Можно ли использовать для своего рода простой полиморфизм?

someFunc :: (Show a, Show b) => Either a b -> IO() 
someFunc (Left x) = print $ "Left " ++ show x 
someFunc (Right x) = print $ "Right " ++ show x 

main = do 
    someFunc (Left "Test1") 
    someFunc (Right "Test2") 

Однако, это дает,

Ambiguous type variable `b0' in the constraint: 
     (Show b0) arising from a use of `someFunc' 
    Probable fix: add a type signature that fixes these type variable(s) 
    In a stmt of a 'do' expression: someFunc (Left "Test1") 

и

Ambiguous type variable `a0' in the constraint: 
     (Show a0) arising from a use of `someFunc' 
    Probable fix: add a type signature that fixes these type variable(s) 
    In the expression: someFunc (Right "Test2") 

Если я правильно понимаю, когда я вызываю функцию с Left x, он жалуется, потому что это Безразлично Не знаю тип варианта Right x и наоборот. Однако эта ветвь функции не используется. Есть лучший способ сделать это?

ответ

3

Хорошо, это сильно зависит от того, что вы пытаетесь сделать, не так ли? Как вы уже выяснили, для использования конструктора Left вам необходимо знать тип, который он создает. И полный тип требует информации как на a, так и на b.

Лучшим способом достижения полиморфизма в Haskell является использование классов типов. Вы можете легко обеспечить различные реализации «методов» для разных экземпляров.

Хорошее сравнение концепций классов объектов и типов классов можно найти here.

5

Вы можете сделать эту работу, явно указав другой тип:

main = do 
    someFunc (Left "Test1" :: Either String()) 
    someFunc (Right "Test2" :: Either() String) 

, но я согласен с x13n, что это, вероятно, не самый лучший способ, чтобы сделать то, что вы пытаетесь сделать. Обратите внимание, что someFunc функционально идентичен

someFunc :: (Show a) => Bool -> a -> IO() 
someFunc False x = print $ "Left " ++ show x 
someFunc True x = print $ "Right " ++ show x 

потому что единственная информация, которую вы проистекают из структуры Either является ли это в Left или Right. Эта версия также не требует указания типа заполнителя при его использовании.

+0

На вопрос, цель использования «Либо» должна была получить форму ** полиморфизма **. Альтернатива boolean-tag, которую вы предоставляете, не делает этого. Выбор между «Left» и «Right» также должен выбирать между двумя типами. –

+0

@sacundim: Ну, шаблоны вызова моего 'someFunc' идентичны' someFunc' в вопросе. Независимо от того, в чем заключалось намерение, мое определение * - это то, что было достигнуто; если то, что есть, не то, что нужно, то это также указывает на проблему в оригинальном решении :) – ehird

5

Это хороший вопрос, потому что он заставлял меня думать о , почему Haskell ведет себя так.

class PseudoArbitrary a where 
    arb :: a 

instance PseudoArbitrary Int where 
    arb = 4 

instance PseudoArbitrary Char where 
    arb = 'd' 

instance PseudoArbitrary Bool where 
    arb = True 

reallyDumbFunc :: (PseudoArbitrary a, PseudoArbitrary b) => 
        Either a b -> Either a b 
reallyDumbFunc (Left x) = Right arb 
reallyDumbFunc (Right x) = Left arb 

Так что проверьте это. Я сделал typeclass PseudoArbitrary, где экземпляры typeclass предоставляют (псевдо) произвольный элемент своего типа. Теперь у меня есть reallyDumbFunc Тион, который принимает Either a b, где оба a и b имеют PseudoArbitrary экземпляров, и если Left был поставлен, я производить Right с (псевдо-) произвольное значение типа b в нем, и наоборот , Итак, теперь давайте сыграем в ghci:

ghci> reallyDumbFunc (Left 'z') 
Ambiguous type variable blah blah blah 
ghci> reallyDumbFunc (Left 'z' :: Either Char Char) 
Right 'd' 
ghci> reallyDumbFunc (Left 'z' :: Either Char Int) 
Right 4 

Whoa! Несмотря на то, что все, что я изменил, было типа ввода, оно полностью изменило тип и значение вывода! Вот почему Haskell не может решить эту двусмысленность сам по себе: потому что это потребует сложного анализа вашей функции, чтобы убедиться, что вы не делаете такие вещи, как reallyDumbFunc.

Смежные вопросы