Проходим для данных Константа a b = Константа a проходит быструю проверку, но ведет себя странно

Книга haskell хочет, чтобы я реализовал проходимый экземпляр для

newtype Constant a b = Constant { getConstant :: a }

включая все необходимые суперклассы. Код ниже проходит Quickcheck/Checkers, но выглядит забавно

import Test.QuickCheck
import Test.QuickCheck.Checkers
import Test.QuickCheck.Classes

newtype Constant a b = Constant { getConstant :: a }

instance Functor (Constant a) where
  fmap f (Constant a) = Constant a

instance Foldable (Constant a) where
  foldr f z (Constant x) = z

instance Traversable (Constant a) where
  traverse f (Constant a) = pure $ Constant a    

type TI = []
main = do
  let trigger = undefined :: TI (Int, Int, [Int])
  quickBatch (traversable trigger)

Когда я пытаюсь использовать проходимый экземпляр следующим образом:

traverse (\x -> [x + 1]) $ Constant 5 

Я не получаю Constant [5], на который я надеялся, а скорее

traverse (\x -> [x + 1]) $ Constant 5
  :: (Num b, Num a) => [Constant a b]

Что это значит? Я сделал что-то не так?


person The Unfun Cat    schedule 26.05.2016    source источник
comment
Коан Haskell: Constant 5 не содержит константы 5.   -  person Daniel Wagner    schedule 26.05.2016


Ответы (1)


Когда я пытаюсь использовать проходимый экземпляр следующим образом:

traverse (\x -> [x + 1]) $ Constant 5 

Я не получаю Constant [5], на что надеялся [...]

Вы не получите Constant [5]. Напишем тип для traverse:

traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)

... и выровняйте его с вашим экземпляром:

                  -- I've substituted `x` for `a` and `y` for `b` in the
                  -- first type, because otherwise my head hurts.
                  (x -> f  y) -> t            x -> f  (t            y)
(Num a, Num b) => (a -> [] a) -> (Constant b) a -> [] ((Constant b) a)

Итак, у нас есть:

t = Constant b
f = []
x = Num a => a
y = NUm b => b

Обратите внимание, что тип traverse подразумевает, что t будет одним и тем же в аргументе и результате. Поскольку вы используете Constant 5 :: Num a => Constant a b в качестве аргумента, это означает, что вы никогда не сможете получить Constant [5] :: Num a => Constant [a] b в результате, потому что Constant a /= Constant [a].

person Luis Casillas    schedule 26.05.2016
comment
Спасибо, будем думать. Моя реализация правильная? - person The Unfun Cat; 26.05.2016
comment
Тем не менее, он не возвращает Constant 5 или что-то конкретное, он просто возвращает тип с ограничением или что-то подобное... - person The Unfun Cat; 26.05.2016
comment
@TheUnfunCat: Ваша реализация выглядит правильной. Вот одно упражнение, которое вы могли бы попробовать оценить: как бы вы написали другой экземпляр Traversable для Constant? (Подсказка: это вопрос с подвохом!) В любом случае, ваш пример должен оцениваться как значение [Constant 5]; вы должны быть в состоянии сопоставить образец с результатом, чтобы убедиться в этом. В этом суть функтора Constant — это предельный случай для Functor/Applicative/Traversable, который просто игнорирует функции, которые вы на него отображаете. - person Luis Casillas; 26.05.2016