Est-il possible d'exporter des constructeurs pour l'appariement des motifs, mais pas pour la construction, dans les Modules Haskell?

un type de données vanille dans Haskell a zéro ou plus constructeurs, chacun d'eux joue deux rôles.

dans les expressions, il supporte l'introduction, c'est une fonction de zéro ou plus d'arguments au type de données.

dans patterns, il supporte l'élimination, c'est un peu comme une fonction du type de données à Maybe (tuple des types d'arguments).

est-il possible pour une signature de module de cacher la première tout en exposant la seconde?

Le cas d'utilisation est-ce: J'ai un type, T, dont les types de constructeurs seuls peuvent parfois être utilisés pour construire des non-sens. J'ai des fonctions de construction qui peuvent être utilisées pour construire des instances du type qui sont garanties de ne pas être absurde. Il serait logique de cacher les constructeurs dans ce cas, mais il serait toujours utile que les appelants soient en mesure de faire correspondre les motifs sur le non-non-sens garanti qu'ils construisent avec les fonctions de construction.

je soupçonne que c'est impossible, mais au cas où quelqu'un a un moyen pour le faire, je pensais que je demanderais.

la meilleure chose suivante est de cacher les constructeurs et de créer un tas de fonctions à partir de T -> peut-être (ceci, cela), T - > peut-être (le, autre, chose), etc.

25
demandé sur Doug McClean 2011-11-17 22:46:50
la source

3 ответов

Vous pouvez utiliser un type de vue et voir les modèles pour faire ce que vous voulez:

module ThingModule (Thing, ThingView(..), view) where

data Thing = Foo Thing | Bar Int

data ThingView = FooV Thing | BarV Int

view :: Thing -> ThingView
view (Foo x) = FooV x
view (Bar y) = BarV y

Notez que ThingView n'est pas un type de données récursives: tous les constructeurs de valeurs renvoient à Thing. Donc maintenant vous pouvez exporter les constructeurs de valeur de ThingView et garder Thing abstrait.

Utiliser comme ceci:

{-# LANGUAGE ViewPatterns #-}
module Main where

import ThingModule

doSomethingWithThing :: Thing -> Int
doSomethingWithThing(view -> FooV x) = doSomethingWithThing x
doSomethingWithThing(view -> BarV y) = y

La flèche de la notation des choses est de GHC Voir Les Modèles. Notez qu'il faut une langue pragma.

bien sûr, vous n'êtes pas obligé d'utilisation afficher les modèles, vous pouvez faire tous les desugaring à la main:

doSomethingWithThing :: Thing -> Int
doSomethingWithThing = doIt . view
  where doIt (FooV x) = doSomethingWithThing x
        doIt (BarV y) = y

Plus

en fait nous pouvons faire un peu mieux: il n'y a aucune raison de dupliquer tous les constructeurs de valeurs pour les deux Thing et ThingView

module ThingModule (ThingView(..), Thing, view) where

   newtype Thing = T {view :: ThingView Thing}
   data ThingView a = Foo a | Bar Int

continuer à l'utiliser de la même façon qu'avant, mais maintenant les correspondances peuvent utiliser Foo et Bar.

{-# LANGUAGE ViewPatterns #-}
module Main where

import ThingModule

doSomethingWithThing :: Thing -> Int
doSomethingWithThing(view -> Foo x) = doSomethingWithThing x
doSomethingWithThing(view -> Bar y) = y
36
répondu Lambdageek 2011-11-17 23:24:48
la source

à Partir de GHC 7.8, vous pouvez utiliser PatternSynonyms exporter des modèles indépendants des constructeurs. La réponse de @Lambdageek serait donc analogue à celle de @ Lambdageek!--8-->

{-# LANGUAGE PatternSynonyms #-}

module ThingModule (Thing, pattern Foo, pattern Bar) where

pattern Foo a <- RealFoo a
pattern Bar a <- RealBar a

data Thing = RealFoo Thing | RealBar Int

et

{-# LANGUAGE PatternSynonyms #-}
module Main where

import ThingModule

doSomethingWithThing :: Thing -> Int
doSomethingWithThing (Foo x) = doSomethingWithThing x
doSomethingWithThing (Bar y) = y

donc ça ressemble à des constructeurs normaux.

Si vous essayez d'utiliser Bar pour construire une valeur, vous obtenez

Main.hs:9:32:
    Bar used in an expression, but it's a non-bidirectional pattern synonym
    In the expression: Bar y
20
répondu Joachim Breitner 2014-08-06 17:31:47
la source

Vous ne pouvez pas. Mais s'il n'y a qu'un nombre raisonnable de constructeurs pour votre type T, vous pouvez vouloir cacher les constructeurs et fournir à la place une fonction qui fait correspondre le motif dans le même esprit que maybe :: b -> (a -> b) -> Maybe a -> b.

8
répondu Tsuyoshi Ito 2011-11-17 22:51:33
la source

Autres questions sur