2012-01-31 2 views
3

У меня есть куча функций, как:Haskell - обертки типа объединительных

f1 :: String -> String -> ... -> String ->() 
f1 a b ... z = g [("a", a), ("b", b), ... ("z", z)] 
... 
fn :: String -> Int -> String -> ... -> String ->() 
fn a b ... z = g [("a", a), ("b", show b), ... ("z", z)] 

Так пользователь может просто назвать их как f1 "abc" "def". Я не хочу, чтобы он это делал, потому что он мог легко поменять «abc» и «def» по ошибке (и Бог знает, сколько времени будет потрачено впустую при отладке). Я хочу, чтобы он передать аргументы как fk (A "abc") (B "def") Насколько я могу видеть, есть 2 варианта:

  1. Массивная конструкция data и массивные распаковывать функции:

    data Value = A String 
          | B String 
          | C Int 
          | D String 
          ... 
    
    unpack :: Value -> String 
    unpack (A a) = a 
    unpack (B b) = b 
    unpack (C c) = show c 
    unpack (D c) = d 
    

    участки кода.

  2. Общие и Newtypes класс типов:
    EDIT: Хорошо, мы можем использовать GeneralizedNewtypeDeriving в таком простом случае.

    {-# LANGUAGE GeneralizedNewtypeDeriving #-} 
    
        class Value a where 
        unpack :: a -> String 
        instance Value String where 
        unpack = id 
        instance Value Int where 
        unpack = show 
    
        newtype A = A String deriving Value 
        newtype B = B String deriving Value 
        newtype C = C Int deriving Value 
        newtype D = D String deriving Value 
    
        ... 
    

    выглядит гораздо лучше, но все fk будет выглядеть

    fk a b ... z = g [("a", unpack a), ("b", unpack b), ... ("z", unpack z)] 
    

    Много кода и дублирования.

То, что я хочу, это какая-то магия трюк, который позволил бы мне:

  1. fk a b ... z = g [("a", a), ("b", b), ... ("z", z)]
  2. g = h . map (second unpack)

ответ

2

Я думаю, что проблемы сводится к следующему: Список может иметь элементы одного типа; это означает, что либо вам нужно «объединить» его в один тип в вашем f, либо вы не можете полагаться на проверки типа haskells. Например. следующий код будет работать для вас, но проверить тип среды выполнения:

{-# LANGUAGE GADTs #-} 

import Control.Arrow (second) 

data Item where 
    A :: String -> Item 
    B :: Int -> Item 

unpack (A s) = s 
unpack (B i) = show i 

myf [email protected](A {}) [email protected](B {}) [email protected](B {}) = 
    let g = [("a", a), ("b", b), ("c", c)] 
    in map (second unpack) g 
myf _ _ _ = error "Bad types" 

main = do 
    putStrLn $ show $ myf (A "test") (B 13) (B 14) 
    putStrLn $ show $ myf (A "test") (B 13) (A "xxx") 

Если вы хотите во время компиляции проверки типов, вы можете сделать что-то подобное; однако вам все равно придется перепечатывать параметры в один и тот же тип, поэтому в некотором смысле разница между его распаковкой невелика, но может быть только немного менее подвержена ошибкам. Хороший трюк приходит из пакетов JSon - они переопределять некоторый оператор (например, = :) для создания типа, так что вы бы:

{-# LANGUAGE ExistentialQuantification #-} 
import Control.Arrow (second) 

class Value a where 
    unpack :: a -> String 
newtype A = A String 
newtype B = B Int 

instance Value A where 
    unpack (A a) = a 

instance Value B where 
    unpack (B b) = show b 

data Item = forall b. Value b => Item b 
a =: b = (a, Item b) 

myf :: A -> B -> B -> [(String, String)] 
myf a b c = 
    let g = ["a" =: a, "b" =: b, "c" =: c] 
    in map (second (\(Item x) -> unpack x)) g 

main = do 
    putStrLn $ show $ myf (A "test") (B 13) (B 14) 

Это не так уж сильно отличается от простого определения a =: b = (a, unpack b) хотя.

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