Подходит для определенного пользователем типа - PullRequest
2 голосов
/ 09 марта 2020

Я пытаюсь написать Applicative для этого типа

data Choice a = ColumnA a | ColumnB a

Я написал экземпляр Functor:

instance Functor Choice where 
  fmap f (ColumnA a ) = (ColumnA (f a) )
  fmap f (ColumnB a ) = (ColumnB (f a) ) 

Теперь я хочу написать Applicative, где рассматривается ColumnB " правильное значение "и ColumnA считается некоторой ошибкой.

Я пытался

instance Applicative Choice where
    pure             =  ColumnB  
    ColumnB f  <*>  r  =  fmap f r
    ColumnA f  <*>  _  =  ColumnA  f   --- this does not work 

Как я могу заставить его работать?

Ответы [ 2 ]

2 голосов
/ 10 марта 2020

Давайте переименуем ваши конструкторы данных в express ваше намерение правильно, так как

data Choice a = Bad a | Good a

Ваш Functor экземпляр сохраняет порчу на значениях,

instance Functor Choice where 
  fmap f (Bad  x)  =  Bad  (f x) 
  fmap f (Good x)  =  Good (f x) 

, поэтому давайте просто сделайте то же самое для Applicative, не скупясь на наши пункты:

instance Applicative Choice where
    pure              x  =  Good    x     -- fmap f == (pure f <*>) is the Law
    Good f  <*>  Good x  =  Good (f x)
    Good f  <*>  Bad  x  =  Bad  (f x)
    Bad  f  <*>  Good x  =  Bad  (f x)
    Bad  f  <*>  Bad  x  =  Bad  (f x)

Как указывалось в комментариях, это интерпретирует Choice a как изоморф c Writer All a, что означает Choice a значения на самом деле такие же, как (Bool, a), где (False, x) соответствует Bad x и (True, x) соответствует Good x. Естественно, мы считаем значения Good только в том случае, если все в их происхождении было Good.

2 голосов
/ 09 марта 2020

Если ColumnA считается некоторой ошибкой, вы не можете позволить ей обернуть значение a. Верно. Идея (<*>) состоит в том, что он принимает Choice (x -> y) и Choice x и возвращает Choice y. Но если у вас есть ColumnA, заключающий в себе функцию типа x -> y, и у вас справа Choice x, то он, таким образом, должен вернуть Choice y, а не Choice x.

То, что вы можете сделать, это определить тип с двумя параметрами типа, например:

data Choice <b>a b</b> = ColumnA <b>a</b> | ColumnB <b>b</b>

, тогда вы будете выполнять сопоставление только с конструктором данных ColumnB b:

instance Functor (Choice <b>a</b>) where
    fmap _ (ColumnA e) = ColumnA e
    fmap f (ColumnB x) = ColumnB (f x)

и тогда мы можем определить экземпляр Applicative как:

instance Applicative (Choice <b>a</b>) where
    pure = ColumnB
    ColumnB f <*> ColumnB x = ColumnB <b>(f x)</b>
    ColumnA e <*> _ = ColumnA e
    _ <*> ColumnA e = ColumnA e

Такой экземпляр для Functor и Applicative, однако, уже существует: так он определен в Either тип данных .

...