Хорошая стратегия здесь - следовать типам. GH C может помочь нам в этом, если мы напишем неполные определения с напечатанными дырами :
newtype F2 x a = F2 ((a -> x) -> a)
instance Functor (F2 x) where
fmap f (F2 g) = _
Если мы попытаемся скомпилировать это, GH C сообщит:
Diatonic.hs:275:21: error:
• Found hole: _ :: F2 x b
Where: ‘b’ is a rigid type variable bound by
the type signature for:
fmap :: forall a b. (a -> b) -> F2 x a -> F2 x b
at Diatonic.hs:275:5-8
‘x’ is a rigid type variable bound by
the instance declaration
at Diatonic.hs:274:10-23
• In the expression: _
In an equation for ‘fmap’: fmap f (F2 g) = _
In the instance declaration for ‘Functor (F2 x)’
• Relevant bindings include
g :: (a -> x) -> a (bound at Diatonic.hs:275:16)
f :: a -> b (bound at Diatonic.hs:275:10)
fmap :: (a -> b) -> F2 x a -> F2 x b (bound at Diatonic.hs:275:5)
|
275 | fmap f (F2 g) = _
|
Это означает, что мы должны заполнить отверстие значением F2 x b
. Единственный способ создать его - использовать конструктор (для следующих шагов я опущу неизменный шаблон):
fmap f (F2 g) = F2 _
• Found hole: _ :: (b -> x) -> b
Итак, нам нужна функция, которая принимает b -> x
, Мы можем создать один. Давайте назовем его аргумент k
и посмотрим, сможем ли мы завершить его тело:
fmap f (F2 g) = F2 (\k -> _)
• Found hole: _ :: b
Единственное, что может создать b
, это f :: a -> b
:
fmap f (F2 g) = F2 (\k -> f _)
• Found hole: _ :: a
Единственное, что может создать a
, это g :: (a -> x) -> a
:
fmap f (F2 g) = F2 (\k -> f (g _))
• Found hole: _ :: a -> x
Мы должны предоставить функцию a -> x
, поэтому давайте добавим еще одну лямбду (возможный ярлык см. в заключительной части ответа):
fmap f (F2 g) = F2 (\k -> f (g (\a -> _)))
• Found hole: _ :: x
Единственное, что может произвести x
, это k :: b -> x
fmap f (F2 g) = F2 (\k -> f (g (\a -> k _)))
• Found hole: _ :: b
Единственное, что может создать b
, это f :: a -> b
:
fmap f (F2 g) = F2 (\k -> f (g (\a -> k (f _))))
• Found hole: _ :: a
Если мы попытаемся использовать g
для создания значения a
, как мы делали несколько шаги go, мы попадем в ловушку замкнутого круга. Однако нам не нужно делать это на этот раз, так как аргумент a
лямбды находится в области видимости:
fmap f (F2 g) = F2 (\k -> f (g (\a -> k (f a))))
И мы закончили.
(Это стоит упоминая, что ваш F2
предоставляется преобразователями , , где он известен как Select
.)
Определение, возможно, выглядит немного опрятнее, если мы напишем \a -> k (f a)
pointfree:
instance Functor (F2 x) where
fmap f (F2 g) = F2 (\k -> f (g (k . f)))
Возможно, мы пропустили прямо из отверстия a -> x
сюда, заметив, что наш единственный жизнеспособный вариант - составление f :: a -> b
и k :: b -> x
.