2012-01-31 5 views
3

J'ai un tas de fonctions comme:Haskell - emballages de type unification

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)] 

Ainsi, l'utilisateur peut les appeler comme f1 "abc" "def". Je ne veux pas qu'il le fasse parce qu'il peut facilement échanger "abc" et "def" par erreur (et Dieu sait combien de temps serait gaspillé pendant le débogage). Je veux lui transmettre des arguments comme fk (A "abc") (B "def") Pour autant que je peux voir, il y a 2 options:

  1. Massive construction data et fonction massive Déballez:

    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 
    

    beaucoup de code.

  2. communs et newtypes classe de types:
    EDIT: D'accord, alors, nous pouvons utiliser GeneralizedNewtypeDeriving dans ce cas simple.

    {-# 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 
    
        ... 
    

    est beaucoup mieux mais tous fk ressemblerait

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

    Beaucoup de code et la duplication.

Ce que je veux est un truc magique qui me permettrait:

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

Répondre

2

Je pense que les problèmes se résume à ceci: La liste peut comporter des éléments du même type seulement; ce qui signifie que vous devez soit «fusionner» en un seul type dans votre f, soit vous ne pouvez pas vous fier aux contrôles de type haskells. Par exemple. le code suivant pour vous, mais le contrôle de type est l'exécution:

{-# 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") 

Lorsque vous voulez la compilation vérification de type, vous pouvez faire quelque chose comme ça; Cependant, vous devez toujours retaper les paramètres au même type, donc en un certain sens, il n'y a pas beaucoup de différence entre le déballage, mais seulement un peu moins sujet aux erreurs. Un truc sympa vient des forfaits JSON - ils redéfinissent certains opérateurs (par exemple :) = pour créer le type, vous auriez:

{-# 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) 

Ce n'est pas très différent de simplement définir a =: b = (a, unpack b) bien.