Как «распаковать» структуру в haskell - PullRequest
10 голосов
/ 20 декабря 2009

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

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

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)

Ответы [ 3 ]

4 голосов
/ 20 декабря 2009

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

data Rgb = Rgb !Double !Double !Double

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

3 голосов
/ 20 декабря 2009

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

setSource `applyRgb` rgb

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

2 голосов
/ 20 декабря 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
...