Используйте библиотеку линз 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 $ и берет сеттер из правой руки и использует его на результат левой руки.

Другие вопросы по тегам