Как распаковать структуру в Haskell

Недавно я столкнулся с этой проблемой и нашел решение, но мне интересно, есть ли лучшие (или просто более идиоматические) решения.

У меня есть структура для цвета:

data Rgb = Rgb Double Double Double

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

setSourceRGB :: Double -> Double -> Double -> Render ()

Поэтому мне нужно как-то «распаковать» эту структуру данных, так как setSourceRGB не принимает Rgb. Я нашел два пути. Один из них — определить функцию для применения содержимого Rgb:

applyRgb :: (Double -> Double -> Double -> t) -> Rgb -> t
applyRgb f (Rgb r g b) = f r g b

Тогда я могу сделать:

applyRgb setSourceRGB rgb

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

(\z -> (case z of (Rgb r g b) -> setSourceRGB r g b)) rgb

Однако я не совсем доволен этим, почему-то применение функции только для передачи некоторых значений кажется неправильным. Я хотел бы иметь возможность перевернуть его и «преобразовать» Rgb в правильный тип для setSourceRGB. К сожалению, мне кажется, что невозможно иметь функцию

fromRgb :: Rgb -> Double -> Double -> Double

который можно передать setSourceRGB. Возможно, applyRgb - лучшее решение, но мне интересно, есть ли лучший способ, который позволит мне выразить это так:

setSourceRGB (fromRgb rgb)

person Steve    schedule 20.12.2009    source источник
comment
Что с case .. of? (\(Rgb r g b) -> SetSource r g b) rgb   -  person ephemient    schedule 20.12.2009


Ответы (3)


Нет, вы не можете написать что-то вроде setSourceRGB (fromRgb rgb), потому что это просто передаст функции один аргумент, так что applyRgb кажется лучшим решением. Если вам нравятся такие вещи, вы также можете использовать applyRgb в качестве инфиксной функции:

setSource `applyRgb` rgb

Если вы часто используете эту функцию, вы можете облегчить чтение кода, определив имя для applyRgb setSource.

person gnomnain    schedule 20.12.2009

Кстати, вы почти наверняка должны иметь:

data Rgb = Rgb !Double !Double !Double

вместо этого и скомпилируйте с -funbox-strict-fields, чтобы компоненты можно было распаковать в примитивные двойные значения без распределения.

person Don Stewart    schedule 20.12.2009

Вы не можете «распаковать» что-либо в несколько аргументов, не обернув саму функцию способами, которые вы выяснили.

Однако для согласованности я бы, вероятно, назвал помощника примерно так.

-- from Prelude...
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry f (a, b) = f a b

-- yours?
uncurryRgb :: (Double -> Double -> Double -> a) -> Rgb -> a
uncurryRgb f (Rgb r g b) = f r g b
person ephemient    schedule 20.12.2009
comment
Вы должны исправить определение, чтобы оно соответствовало подписи. Сейчас случаи разные. - person Martijn; 20.12.2009
comment
Ой, опечатка. Ну, помимо различий в правописании, это то же определение, что и applyRgb ОП... - person ephemient; 20.12.2009