2016-04-28 3 views
1

У меня проблема. У меня есть тип данных Thing, который по сути является большой записью «Возможно, парные» или «Возможно, тексты». Я хочу создать тип данных, который может захватить идею выполнения вычислений на нем, хотя некоторые поля могут быть Nothing.Haskell: Применимо для кортежей

data Computation a b = Computation 
    { getInputs :: Thing -> a 
     computation :: a -> b 
     modifyThing :: b -> Thing -> Thing 
    } 

Основываясь на выходе Ъ, что, скорее всего, соответствует некоторому полю в Thing, я хочу, чтобы создать новую вещь.

modifyThing :: b -> Thing -> Thing 

Вычисление от Thing к Может б можно разделить на две части, загрузка переменных и вычисление, которое просто принимает номера или тексты.

getInputs :: Thing -> a 

computation :: a -> b 

Вышеуказанное почти то, что я хочу. В этом случае a будет (возможно, a1, Maybe a2 ..) и так далее. Это означает, что в «вычисления» и getInputs я должен сделать что-то вроде

getInputs t = \t -> (getProp1 t , getProp2 t) 

computation = \(m_a1, m_a2) -> do 
    a1 <- m_a1 
    a2 <- m_a2 
    return $ a1 + a2 

Я предпочел бы иметь вычисление просто смотреть, как

, а затем сделать что-то вроде

runComputation :: Thing -> Computation -> b 
runComputation thing comp = magic (computation comp) ((getInputs comp) thing) 
    where magic = ??? 

Проблема в том, что я не знаю, как это сделать, начиная с

(Maybe a1, Maybe a2, ... , Maybe a_n) 

в

a1 -> a2 -> ... -> a_n 

Если какой-либо из Maybe являются не то просто не возвращают ничего. Я могу сделать

pure computation <*> m_a1 <*> m_a2 <*> m_a3 

но как я могу написать магию для работы с любым типом кортежа?

P.S.

Я думал о написании вычисления, как

computation :: Thing -> b 

и покончив с getInputs, но мне кажется, было бы гораздо более unweildy для меня, чтобы проверить и играть. Вот почему я пытаюсь использовать подход, описанный выше. Считаете ли вы, что это хорошая идея, что я сделал?

отредактировал

Хотя не решение конкретного вопроса, я спросил, но с намерением, что я пытался сделать, я решил, что делать

data Computation = Computation 
    { getInputs :: Thing -> Maybe a 
    , computation :: a -> b 
    , modifyThing :: b -> Thing -> Thing 
    } 

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

+0

Есть ли какой-либо причине вы не можете использовать список, может быть, это? – Guvante

+0

@ Guvante Один из недостатков списков состоит в том, что они являются однородными - например, невозможно было бы выбрать одновременно поле «Double»-y и поле ввода «Int'-y». –

+0

@ user3550758 Если вы найдете решение, способ StackOverflow должен записать его в качестве ответа, а не включать его в редактирование вопроса. Я призываю вас разделить свое «редактирование» на свою собственную сущность и даже принять его, если это лучший ответ. (Это считается совершенно вежливым и даже желательно здесь.) –

ответ

0

Я не совсем уверен, что польза от использования Computation вместо Thing -> Thing, но вы должны быть в состоянии определить функции, о которых идет речь довольно легко. Прежде всего, ваша примерная функция, computation, может быть переписана в несколько более удобной форме.(На самом деле вы должны называть это чем-то другим, поскольку имя computation уже принято вашим регистратором).

exampleComp :: Num a => (Maybe a, Maybe a) -> Maybe a 
exampleComp (a1, a2) = liftA2 (+) a1 a2 

Это еще не так удобно, как простое дополнение, но оно приближается. Ваша функция runComputation также довольно проста, так как это действительно сводится к составлению трех компонентов вашего типа Computation.

runComputation :: Thing -> Computation -> Thing 
runComputation thing (Computation ins comp modify) = modify (comp $ ins thing) thing 

Или, написанный с использованием записей, выглядит так.

runComputation thing comp = modifyThing comp 
          (computation comp $ getInputs comp thing) 
          thing 

В основном, эта функция извлекает соответствующие данные с помощью getInputs, передает эти данные в computation, чтобы получить новый набор данных, а затем передает что к modifyThing на самом деле изменить объект.

Без дополнительной информации о том, что вы планируете делать с вашим типом данных, трудно сказать, какой метод вы должны использовать. Лично я не вижу причины разделять computation и modifyThing, но я действительно не вижу причин использовать Computation в первую очередь. Несмотря на это, это больше вопрос CodeReview, чем для StackOverflow.

1

Проблема заключается в том, что я не знаю, как это сделать перейти от

(Maybe a1, Maybe a2, ... , Maybe a_n) 

в

a1 -> a2 -> ... -> a_n 

Похож-иш к uncurry, который принимает обычную функцию и делает версию, которая берет кортеж вместо нескольких аргументов.

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

Этот конкретный пример:

computation = \(m_a1, m_a2) -> do 
    a1 <- m_a1 
    a2 <- m_a2 
    return $ a1 + a2 

может быть сделано с Applicative как так

computation (m_a1, m_a2) = (+) <$> m_a1 <*> m_a2 

Но вы можете абстрактные это что-то вроде uncurry, как это:

uncurryA :: Applicative f => (a -> b -> c) -> (f a, f b) -> f c 
uncurryA f (a, b) = f <$> a <*> b 

Разрешение computation быть определены следующим образом:

computation a_b = uncurryA (+) a_b 
+0

'uncurry' работает только с функциями с двумя аргументами. Вы можете сделать какую-то рекурсивную неуправляемость (поскольку, например, «uncurry. Uncurry» может превратить 'a -> a -> a -> b' в' ((a, a), a) -> b'), но вам все равно придется выяснить, как сгладить полученный вложенный кортеж. – chepner

+0

(Или, что более точно, 'uncurry' обрабатывает только первые два аргумента за раз, что приводит к вложенному кортежу, если' uncurry' используется несколько раз подряд.) – chepner

Смежные вопросы