Используйте библиотеку линз Haskell, чтобы отобразить линзу
В приведенном ниже коде мой вопрос касается самой верхней функции someFunc
(все, что ниже, это просто полный пример). Я использую get-синтаксис записи и fmap
там. Как линзовый способ реализовать someFunc
?
import Control.Lens
import Data.IntMap (IntMap)
someFunc :: Farm -> IntMap Size
someFunc farm =
_barnSize <$> farm ^. farmBarns
data Farm = Farm
{ _farmBarns :: IntMap Barn
}
farmBarns :: Lens' Farm (IntMap Barn)
farmBarns = lens _farmBarns (\farm barns -> farm { _farmBarns = barns } )
type Size = (Int, Int)
data Barn = Barn
{ _barnSize :: Size
}
barnSize :: Lens' Barn Size
barnSize = lens _barnSize (\barn size -> barn { _barnSize = size } )
2 ответа
Просто замени _barnSize
от (^. barnSize)
или, что эквивалентно, view barnSize
:
someFunc :: Farm -> IntMap Size
someFunc farm = view barnSize <$> farm ^. farmBarns
Для решения "100% объектив " вы можете использовать mapped
сеттер. В этом случае, однако, я не думаю, что в этом есть какое-то реальное преимущество.
someFunc :: Farm -> IntMap Size
someFunc farm = (mapped %~ view barnSize) (farm ^. farmBarns)
Другое возможное написание включает использование to
объединить все в одном добытчике. Это тоже мало что дает, но вам может быть удобно, если вы хотите продолжать работать с IntMap
в стиле линз, соединяя дополнительные геттеры / складки / и т.д.
someFunc :: Farm -> IntMap Size
someFunc farm = farm ^. farmBarns . to (fmap (view barnSize))
Существует специальный комбинатор, который объединяет to
/ (^.)
комбинация выше. Это называется views
:
someFunc :: Farm -> IntMap Size
someFunc farm = views farmBarns (fmap (view barnSize)) farm
Ты можешь использовать mapped
(или же traversed
) в вашем случае "отобразить" пройденный объект с помощью линз:
someFunc :: Farm -> IntMap Size
someFunc farm =
farm ^. farmBarns & mapped %~ view barnSize
Это может немного сбивать с толку, но вот что происходит, я добавлю скобки, чтобы было немного понятнее
someFunc :: Farm -> IntMap Size
someFunc farm =
(farm ^. farmBarns) & (mapped %~ (view barnSize))
Так что в основном мы используем farm ^. farmBarns
чтобы получить IntMap Barns
с фермы, на правой стороне &
мы строим сеттер, используя %~
который является инфиксом over
Однако функция, которая over
передает свою цель на самом деле просто фокусируется на barnSize с помощью объектива. view barnSize :: Barn -> Size
,
Наконец, мы используем &
связать все это вместе, что эквивалентно flip $
и берет сеттер из правой руки и использует его на результат левой руки.