2016-07-07 4 views
3

Замена полей имена с буквами, у меня есть дела, как это:Что такое лучший способ управления большими записями Haskell?

Какие методы позволили бы мне лучше управлять этим ростом?

В идеальном мире a, b и c все будут одного типа, поэтому я мог бы хранить их в списке, но они могут быть разных типов. Я особенно заинтересован в том, чтобы сбросить записи без необходимости массированных шаблонов.

Я использую эту большую запись для хранения различных типов в результате permutation parsing the vCard format.

Update

Я реализовал как generics и foldl подходы, предложенные ниже. Они оба работают, и оба они уменьшают three large field lists.

+0

Нужно ли записывать поля записей (может ли 'Foo' быть чем-то другим, кроме записи)? – Alec

+1

Этот запах проблемы XY. Возможно, решение состоит в том, чтобы вообще не иметь такого «Foo», но чтобы сделать это суждение, мы должны предоставить информацию о проблеме, которую «Foo» должен решить. – Bakuriu

+1

Похоже, что это вариант использования для программирования типа данных, возможно, с использованием библиотеки генериков, например, generics-sop. – danidiaz

ответ

2

Некоторые предложения:

(1) Вы можете использовать RecordWildCards расширение автоматически распаковке запись в переменные. Не помогает, если вам нужно распаковать две записи одного типа, но это полезно иметь в виду. У Оливера Чарльза есть хороший пост в блоге: (link)

(2) Кажется, что ваш пример приложения выполняет сгиб по записям. Посмотрите на пакет foldl Gabriel Gonzalez. Существует также блог: (link)

Вот пример того, как можно использовать его с записью, как:

data Foo = Foo { _a :: Int, _b :: String } 

Следующий код вычисляет максимум из _a полей и конкатенации _b_ полей.

import qualified Control.Foldl as L 
import Data.Profunctor 

data Foo = Foo { _a :: Int, _b :: String } 
    deriving (Show) 

fold_a :: L.Fold Foo Int 
fold_a = lmap _a (L.Fold max 0 id) 

fold_b :: L.Fold Foo String 
fold_b = lmap _b (L.Fold (++) "" id) 

fold_foos :: L.Fold Foo Foo 
fold_foos = Foo <$> fold_a <*> fold_b 

theFoos = [ Foo 1 "a", Foo 3 "b", Foo 2 "c" ] 

test = L.fold fold_foos theFoos 

Обратите внимание на использование функции Profunctor lmap извлечь из поля, которые мы хотим, чтобы сгиб. Выражение:

L.Fold max 0 id 

является створка над списком Ints (или любой экземпляр Num), и, следовательно:

lmap _a (L.Fold max 0 id) 

одно и то же раз, но над списком Foo записей, где мы используем _a для изготовления интс.

+1

Я использовал этот подход (https://github.com/rcampbell/vcard/blob/14fb1c91df249ec9aa21ac5cbfe967d7b7a9757d/VCard.hs#L109-L141) в ветке 'foldl', чтобы сравнить его с дженериками. – rcampbell

+0

Это очень приятно! – ErikR

4

Способы программирования общего типа данных могут быть использованы для преобразования всех полей записи в какой-либо «однородный» способ.

Возможно, все поля в записи реализуют некоторый класс, который мы хотим использовать (типичный пример: Show). Или, может быть, у нас есть еще одна запись «аналогичной» формы, которая содержит функции, и мы хотим применить каждую функцию к соответствующему полю исходной записи.

Для этих видов использования библиотека generics-sop является хорошим вариантом. Он расширяет функциональные возможности Generics по умолчанию GGC с дополнительным оборудованием уровня, которое обеспечивает аналоги функций, таких как sequence или ap, но которые работают во всех полях записи.

Используя generics-sop, я попытался создать немного менее подробную версию вашего merge funtion. Некоторый предварительный импорт:

{-# language TypeOperators #-} 
{-# language DeriveGeneriC#-} 
{-# language TypeFamilies #-} 
{-# language DataKinds #-} 

import Control.Applicative (liftA2) 
import qualified GHC.Generics as GHC 
import Generics.SOP 

helper функция, которая поднимает бинарную операцию в виде полезные функциями дженерик-подачка:

fn_2' :: (a -> a -> a) -> (I -.-> (I -.-> I)) a -- I is simply an Identity functor 
fn_2' = fn_2 . liftA2 

Общая функция слияния, которая принимает вектор операторов и работ на любой одной конструктор записи, производный Generic:

merge :: (Generic a, Code a ~ '[ xs ]) => NP (I -.-> (I -.-> I)) xs -> a -> a -> a 
merge funcs reg1 reg2 = 
    case (from reg1, from reg2) of 
     (SOP (Z np1), SOP (Z np2)) -> 
      let npResult = funcs `hap` np1 `hap` np2 
      in to (SOP (Z npResult)) 

Code тип семьи, который возвращает тип уровня Li st списков, описывающих структуру типа данных. Внешний список для конструкторов, внутренние списки содержат типы полей для каждого конструктора.

Часть ограничения указывает, что «тип данных может иметь только один конструктор», требуя, чтобы внешний список имел ровно один элемент.

Образец шаблона (SOP (Z _) извлекает вектор (разнородный) значений полей из общего представления записи. SOP означает «сумма продуктов».

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

data Person = Person 
    { 
     name :: String 
    , age :: Int 
    } deriving (Show,GHC.Generic) 

instance Generic Person -- this Generic is from generics-sop 

mergePerson :: Person -> Person -> Person 
mergePerson = merge (fn_2' (++) :* fn_2' (+) :* Nil) 

В Nil и :* конструкторы используются для построения вектора операторов (тип называется NP, из п-арной продукта). Если вектор не совпадает с количеством полей в записи, программа не будет компилироваться.

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

class Mergeable a where 
    mergeFunc :: a -> a -> a 

instance Mergeable String where 
    mergeFunc = (++) 

instance Mergeable Int where 
    mergeFunc = (+) 

mergePerson :: Person -> Person -> Person 
mergePerson = merge (hcpure (Proxy :: Proxy Mergeable) (fn_2' mergeFunc)) 

hcliftA2 функция (которая объединяет hcpurehap) может быть использована для упрощения дальнейших действий.

+0

Я [реализовал этот подход] (https://github.com/rcampbell/vcard/blob/b377d690854ac088ae8fba078971ac732b08cfb8/VCard.hs#L115-L151) в ветке «generics» для сравнения с foldl one. – rcampbell

+0

@rcampbell Я обновил код, чтобы упростить некоторые вещи. – danidiaz

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

  • Нет связанных вопросов^_^