Следуйте за типами:
fmap f (Baz s g) = GOAL
-- f :: a -> b
-- s :: String
-- g :: Qux -> Foo a
-- GOAL :: Foo b
Итак, мы ищем Foo b
(линия ЦЕЛИ). Мы можем сделать один с Bar
или Baz
. fmap
должен сохранить структуру, поэтому она должна быть Baz
. Аргумент String
для Baz
, вероятно, не должен измениться, это только второй аргумент, который ставит проблему.
fmap f (Baz s g) = Baz s GOAL
-- f :: a -> b
-- s :: String
-- g :: Qux -> Foo a
-- GOAL :: Qux -> Foo b
Теперь мы должны сделать Qux -> Foo b
. Если вам нужно создать функцию, лямбда - важный инструмент (есть и другие, но лямбда - это дедушка). Итак, сделайте лямбду:
fmap f (Baz s g) = Baz s (\x -> GOAL)
-- f :: a -> b
-- s :: String
-- g :: Qux -> Foo a
-- x :: Qux <-- NEW
-- GOAL :: Foo b
Обратите внимание, что теперь у нас есть x :: Qux
для работы. Используя g
и x
, мы можем сделать Foo a
, а затем, используя fmap f
рекурсивно 1 , мы можем сделать необходимое Foo b
.
Обратите внимание, как я просто заполняю выражение одним крошечным шагом за раз, заменяя неизвестные аргументы целями, а затем делаю шаг назад, чтобы рассмотреть, какие переменные у меня есть в области видимости и каковы их типы. Продолжайте делать это, пока путь к цели не станет ясным.
1 Рекурсивный тип обычно имеет соответствующее рекурсивное определение fmap
. Место, в котором происходит рекурсия в fmap
, точно соответствует типу рекурсии.