Подтвердить что ты не робот

Предоставляет ли Haskell способ сопоставить функцию с элементом данных?

Я новичок Haskell, и мне часто приходится разлагать данные с помощью сопоставления с образцом только для применения функции к одному из ее членов, а затем для ее сборки.

Скажем, у меня есть:

data Car = Car { gas :: Int, licensePlate :: String }

и я хочу, чтобы он уменьшил наполовину свой газ, когда он водит, и заправьте его, я делаю:

mapGas:: (Int -> Int) -> Car -> Car
mapGas f (Car aGas aLicensePlate) = Car (f aGas) aLicensePlate

drive:: Car -> Car
drive = mapGas (flip div 2)

refuel:: Int -> Car -> Car
refuel = mapGas . (+)

Есть ли способ сделать это без определения вспомогательной функции mapGas? Так как может стать довольно назойливым, нужно написать функцию отображения для каждого члена данных, когда она сделана из многих полей. Я знаю, что можно назначить значение одному из членов с помощью аксессуаров:

runOutOfFuel:: Car -> Car
runOutOfFuel aCar = aCar { gas = 0 }

Можно ли также сопоставить функцию с аксессуарами? если да, то как?

4b9b3361

Ответ 1

Использование только основных библиотек? Нет. Но с широко используемым пакетом lens, да. Вот что это выглядит в вашем случае:

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens.TH
import Control.Lens

data Car = Car { _gas :: Int, _licensePlate :: String }

makeLenses ''Car

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

runOutOfFuel:: Car -> Car
runOutOfFuel = gas .~ 0

drive:: Car -> Car
drive = gas %~ (`div` 2)

refuel:: Int -> Car -> Car
refuel c = gas +~ c

Магия здесь заключается в том, что makeLenses ''Car генерирует функции gas и licensePlate, которые схожи (но более мощные) с вашим mapGas (фактически, mapGas = (gas %~)). Начало работы с lens довольно сложно, но я рекомендую просто прочитать раздел examples.

Ответ 2

Не существует языковой функции, которая делает это, но, как и во многих вещах в Haskell, основной язык достаточно мощный, что это можно реализовать простым и элегантным способом.

Решение для того, что вы ищете, - это своего рода ценность, называемая объективом. Объектив делает именно то, что вы хотите: он позволяет вам брать любые абстрактные данные и применять функцию на части, получая в результате все значение данных с измененной частью.

Там введение в линзы мне очень нравится здесь. Чтобы использовать приведенные примеры, вам понадобится пакет lens. (Или это, если вы используете Stack)