Подтвердить что ты не робот

Можно ли экспортировать конструкторы для сопоставления шаблонов, но не для построения, в модулях Haskell?

Тип данных ванили в Haskell имеет ноль или более конструкторов, каждый из которых выполняет две роли.

В выражениях он поддерживает введение, его функцию от нуля или более аргументов к типу данных.

В шаблонах он поддерживает исключение, его вроде как функцию из типа данных в Maybe (кортеж типов аргументов).

Возможно ли, чтобы сигнатура модуля спрятала первую часть при экспонировании последнего?

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

Я подозреваю, что это невозможно, но если у кого-то есть способ сделать это, я бы попросил.

Следующее лучшее - скрыть конструкторы и создать кучу функций из T → Maybe (This, That), T → Maybe (The, Other, Thing) и т.д.

4b9b3361

Ответ 1

Вы можете использовать тип вида и шаблоны просмотра, чтобы делать то, что вы хотите:

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

Обратите внимание, что ThingView не является рекурсивным типом данных: все конструкторы значений ссылаются на Thing. Итак, теперь вы можете экспортировать конструкторы значений ThingView и сохранить теги Thing.

Используйте это:

{-# LANGUAGE ViewPatterns #-}
module Main where

import ThingModule

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

Значением нотации стрелок является GHC Просмотр паттернов. Обратите внимание, что для этого требуется языковая прагма.

Конечно, вам не обязательно использовать шаблоны просмотра, вы можете просто сделать все десураринг вручную:

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

Подробнее

На самом деле мы можем сделать немного лучше: нет причин дублировать все конструкторы значений для Thing и ThingView

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

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

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

{-# LANGUAGE ViewPatterns #-}
module Main where

import ThingModule

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

Ответ 2

В GHC 7.8 вы можете использовать PatternSynonyms для экспорта шаблонов независимо от конструкторов. Таким образом, аналогом ответа @Lambdageeks будет

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

и

{-# LANGUAGE PatternSynonyms #-}
module Main where

import ThingModule

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

Таким образом, он выглядит как обычные конструкторы.

Если вы попытаетесь использовать Bar для построения значения, вы получите

Main.hs:9:32:
    Bar used in an expression, but it a non-bidirectional pattern synonym
    In the expression: Bar y

Ответ 3

Вы не можете. Но если для вашего типа T существует только разумное количество конструкторов, вы можете скрыть конструкторы и вместо этого предоставить функцию, которая соответствует шаблону в том же духе, что и maybe :: b -> (a -> b) -> Maybe a -> b.