Как построить fmap для нового типа, который содержит функции - PullRequest
4 голосов
/ 28 октября 2019

Итак, скажем, я хочу определить новый тип, в котором находится функция:

newtype Test m a = Test(m -> (a, m))

Это может быть использовано для хранения состояния какого-либо рода.

Теперь допустим, что я хотел бы реализовать fmap для этого нового типа.

instance Functor (Test m) where
    --fmap :: (a -> b) -> Test m a -> Test m a -> b
    fmap f (Test f1) (Test f2) = ?

Поскольку функции нового типа не позволяют использовать сопоставление с образцом для разделения f1 и f2.

Например, я изначально думал, что, возможно, я мог бы передать входное значение f2 в вызов f1, чтобы получить (a, m) кортеж, но я не думаю, что вы можете сделать это.
т.е.

tup = f1 (get input of f2)

Кто-нибудь может предоставить мне, какие концепции мне не хватает, чтобы справиться с этим сценарием, или это просто невозможно?

Спасибо

ОБНОВЛЕНИЕ

Большое спасибо Реду и Виллему Ван Осему.

Похоже, мне не хватало понимания того, как использовать оператор композиции (.).

По сути, ключом здесь является использование (.), Чтобы получить первый элемент кортежа, а затем выполнить несколько частичных определений функций, чтобы перейти в состояние, необходимое для fmap. Вот моя реализация fmap без использования библиотечной функции. Целенаправленно, многословно, чтобы помочь с пониманием.

alterParamStateHelper :: (a -> b) -> (m -> a) -> m -> (b, m)
alterParamStateHelper f1 f2 m = (b, m)
  where 
    a = f2 e
    b = f1 a

alterParamState :: (a -> b) -> (m -> (a, m)) -> (m -> (b, m))
alterParamState f1 f2 = alterParamStateHelper f1 h1
  where
    h1 = fst . f2--m -> a

instance Functor (Test m) where
  -- fmap :: (a -> b) -> Test m a -> Test m b
  fmap f1 (Test f2) = Test (alterParamState f1 f2)

1 Ответ

6 голосов
/ 28 октября 2019

Так как в домах нового типа функционирует, нет способа использовать сопоставление с образцом для разделения f1 и f2 на части.

Здесь проблема с вашей подписью fmap. fmap имеет подпись fmap :: Functor f => (a -> b) -> f a -> f b, поэтому, если f ~ Test m, то подпись будет fmap :: (a -> b) -> Test m a -> Test m b.

Таким образом, мы можем здесь определить fmap, гдемы "пост-процесс" результат функции. Таким образом, мы создаем новую функцию, которая будет использовать старую функцию для создания 2-кортежа (a, m), а затем вызовем f для первого элемента, так что мы создаем кортеж (b, m):

import Control.Arrow(<b>first</b>)

instance Functor (Test m) where
    fmap f (Test g) = Test (<b>first f . g</b>)
...