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

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

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

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

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

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

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

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

28
задан Doug McClean 17 November 2011 в 18:46
поделиться

2 ответа

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

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

Обозначения стрелок - это View Patterns 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. [1 123]

{-# LANGUAGE ViewPatterns #-}
module Main where

import ThingModule

doSomethingWithThing :: Thing -> Int
doSomethingWithThing(view -> Foo x) = doSomethingWithThing x
doSomethingWithThing(view -> Bar y) = y
38
ответ дан 28 November 2019 в 02:43
поделиться

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

.
8
ответ дан 28 November 2019 в 02:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: