3 votes

Haskell - unification des wrappers de type

J'ai un tas de fonctions comme :

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

Donc l'utilisateur peut juste les appeler comme f1 "abc" "def" . Je ne veux pas qu'il fasse cela parce qu'il peut facilement intervertir "abc" et "def" par erreur (et Dieu sait combien de temps il perdrait en déboguant). Je veux qu'il passe des arguments comme fk (A "abc") (B "def") D'après ce que je vois, il y a 2 options :

  1. Massive data construction et fonction de déballage massif :

    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. Classe de type commune et nouveaux types :
    EDIT : Ok alors, nous pouvons utiliser GeneralizedNewtypeDeriving dans un cas aussi 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
    
      ...

    C'est beaucoup mieux, mais tout fk ressemblerait à

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

    Beaucoup de code et de duplication.

Ce que je veux c'est un tour de magie qui me permettrait :

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

2voto

ondra Points 2491

Je pense que le problème se résume à ceci : La liste ne peut comporter que des éléments de même type, ce qui signifie que vous devez soit la "fusionner" en un type unique dans votre fichier f ou vous ne pouvez pas compter sur les contrôles de type Haskells. Par exemple, le code suivant fonctionnerait pour vous, mais le contrôle de type est exécuté :

{-# 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 a@(A {}) b@(B {}) c@(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")

Si vous voulez une vérification du type au moment de la compilation, vous pouvez faire quelque chose comme ça ; cependant, vous devez toujours retaper les paramètres au même type, donc dans un certain sens, il n'y a pas beaucoup de différence entre le déballage et le déballage, seulement il pourrait être légèrement moins sujet à l'erreur, cependant. Une astuce intéressante provient des paquets json - ils redéfinissent un opérateur (par exemple = :) pour créer le type, de sorte que 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 si différent de définir simplement a =: b = (a, unpack b) cependant.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X