Почему эта проверка типов не выполняется?
compress xs@(_:_:_) = (ifte <$> ((==) <$> head <*> head.tail) <$> ((compress.).(:) <$> head <*> tail.tail) <*> ((:) <$> head <*> compress.tail) ) xs
Приводит к ошибке типа, но я не понимаю, почему. Это должно быть эквивалентно
compress xs@(_:_:_) = (ifte (((==) <$> head <*> head.tail) xs) (((compress.).(:) <$> head <*> tail.tail) xs) (((:) <$> head <*> compress.tail) xs))
, который не делает.
нота: ifte = (\ x y z -> if x then y else z)
, <$>
а также <*>
из Control.Applicative
,
РЕДАКТИРОВАТЬ: ошибка:
Couldn't match expected type `[a]' with actual type `[a] -> [a]'
In the expression:
(ifte <$> ((==) <$> head <*> head . tail)
<$>
((compress .) . (:) <$> head <*> tail . tail)
<*>
((:) <$> head <*> compress . tail))
$ xs
In an equation for `compress':
compress xs@(_ : _ : _)
= (ifte <$> ((==) <$> head <*> head . tail)
<$>
((compress .) . (:) <$> head <*> tail . tail)
<*>
((:) <$> head <*> compress . tail))
$ xs
Я столкнулся с этой проблемой, пытаясь написать бессмысленное решение проблемы 8 из Девяносто девяти проблем Хаскеля. Я пытался сделать это путем изменения точного решения, которое я написал, который был
compress::Eq a => [a]->[a]
compress [] = []
compress (x:[]) = (x:[])
compress (x:y:xs) = ifte ((==) x y) (compress (x:xs)) (x:(compress (y:xs)))
3 ответа
Во-первых, отступ. Во-вторых, рассмотрите возможность использования некоторых переменных.
Даже с более разумным форматированием, вы можете видеть, что это
compress =
ifte <$> ((==) <$> head <*> head.tail)
<$> ((compress.).(:) <$> head <*> tail.tail)
<*> ((:) <$> head <*> compress.tail)
когда это должно быть
compress =
ifte <$> ((==) <$> head <*> head.tail)
<*> ((compress.).(:) <$> head <*> tail.tail)
<*> ((:) <$> head <*> compress.tail)
В-третьих, даже если вы должны быть непостижимым, как насчет
compress (x:r@(y:_)) = ifte (x==y) id (x:) $ compress r
или, указать бесплатно
compress = map fst . filter (uncurry (/=)) . (zip <$> id <*> tail)
Вот ваш код, написанный немного более читабельным способом
{-# LANGUAGE NoMonomorphismRestriction #-}
import Control.Applicative
u = ((==) <$> head <*> head.tail)
v = ((compress.).(:) <$> head <*> tail.tail)
w = ((:) <$> head <*> compress.tail)
ifte = (\ x y z -> if x then y else z)
--compress xs@(_:_:_) = (ifte <$> u <$> v <*> w) xs
compress xs@(_:_:_) = (ifte (u xs) (v xs) (w xs))
Я надеюсь, что теперь вы видите ошибку - правильная версия
--compress xs@(_:_:_) = (ifte <$> u <*> v <*> w) xs
Это очень похоже на то, что уже было сказано, но побалуйте меня на мгновение и позвольте мне проповедовать вам значение типов.
import Control.Applicative
ifte :: Bool -> a -> a -> a
ifte b t f = if b then t else f
compress :: Eq a => [a] -> [a]
-- compress = ifte <$> cond <$> t <*> f
-- We will leave compress undefined so we can load this into ghci.
-- After some trial and error it is clear that this is the part
-- that doesn't type check
compress = undefined
cond :: Eq a => [a] -> Bool
cond = (==) <$> head <*> head . tail
t :: Eq a => [a] -> [a]
t = (compress .) . (:) <$> head <*> tail . tail
f :: Eq a => [a] -> [a]
f = (:) <$> head <*> compress . tail
Здесь я разделил это, и, как упомянул Брэндон, на этом этапе вы должны увидеть, что ошибка заключается в использовании <$>
где <*>
должно быть. Вы освоитесь с этой концепцией, продолжая изучать аппликативный стиль, где ваши выражения обычно имеют один <$>
последовал за произвольным числом <*>
:
f <$> a <*> b <*> c <*> d <*> ...
Игнорируя это понимание, я дал каждому подвыражению здесь тип и TLD, временно. Это позволяет мне загрузить файл в ghci и немного поиграть.
ghci> :t ifte <$> cond <$> t <*> f
... Eq a => [a] -> [a] -> [a]
WTF??? Это типа звук??? Предполагалось, что это даст ошибку, но, видимо, с этим выражением все в порядке. Либо это? Обратите внимание, что подпись этого типа не соответствует той, для которой мы хотимcompress
,
ghci> :t compress
... Eq a => [a] -> [a]
Подвыражения соответствуют сигнатурам типов, которые мы ожидаем от них, о чем свидетельствует тот факт, что компилятор нас не рвет. Поскольку дело обстоит именно так, проблема заключается в том, как мы их объединяем. Так какие части мы хотим объединить здесь? Игнорирование ограничений Eq:
ifte :: Bool -> [a] -> [a] -> [a]
cond :: [a] -> Bool
t :: [a] -> [a]
f :: [a] -> [a]
-- desired result
:: [a] -> [a]
Здесь я сделал тривиальную специализацию ifte
за [a]
вместо любого a
, Отношения ясны: тип вывода cond
, t
, а также f
соответствовать типам ввода ifte
, Нам просто нужно кормить все три из этих выражений одинаково [a]
, Признавая, что (input ->)
Аппликативно обобщаем:
arg1 :: (a -> b -> c -> d)
arg2 :: f a
arg3 :: f b
arg4 :: f c
res :: f d
-- for our case,
-- f = ([a] ->)
-- a = Bool
-- b = [a]
-- c = [a]
-- d = [a]
Стоп... Хугл пора! Hoogling(a -> b -> c -> d) -> f a -> f b -> f c -> f d
мы сразу находим liftA3
, что не удивительно определено:
liftA3 f a b c = f <$> a <*> b <*> c