Haskell/GHC: сопоставление нескольких унарных конструкторов с одним и тем же шаблоном

Поэтому я играл с определением типа данных TrieSet (хотя я знаю, что мне это не нужно ):

module Temp where

import Data.Map

data TrieSet a = Nonterminal (Data.Map a (TrieSet a)) | Terminal (Data.Map a (TrieSet a))

insert :: Ord a => [a] -> TrieSet a -> TrieSet a
insert [] (_ m) = Terminal m
insert (a:as) (c m) = c $ insertWith (insert as . flip const) a (insert as $ Nonterminal empty) m

Когда я получил ошибку, которую я никогда раньше не видел:

% ghc -c Temp.hs
Temp.hs:8:11: Parse error in pattern

Таким образом, казалось, что GHC не любит сопоставлять несколько унарных конструкторов с одним и тем же шаблоном. Я сделал еще один тест, чтобы убедиться, что проблема в нем:

module Temp2 where

extract :: Either String String -> String
extract (_ s) = s

Что, казалось, подтвердило мои подозрения:

% ghc -c Temp2.hs
Temp2.hs:4:9: Parse error in pattern

Итак, мой вопрос (в нескольких частях):

  1. Я прав насчет того, почему GHC не нравятся эти функции?
  2. Есть ли причина, по которой это не будет частью стандарта Haskell? В конце концов, мы можем сопоставить несколько нульарных конструкторов с одним и тем же шаблоном.
  3. Есть ли прагма LANGUAGE, которую я могу дать GHC, чтобы он принял их?

person rampion    schedule 20.08.2011    source источник


Ответы (2)


  1. да. Такие подстановочные знаки никогда не поддерживались.
  2. На мой взгляд, было бы намного сложнее определить тип функции, если вы не знаете конструктор данных, который был сопоставлен. Просто подумайте о функции f (_ n) = n. Каким должен быть его тип? В системе типов Haskell нет возможности описать арность конструкторов типа, поэтому такая функция, как f, не может существовать.
  3. Я так не думаю.
person fuz    schedule 20.08.2011

Если имеет смысл сопоставить два или более конструктора шаблону с подстановочными знаками, вероятно, также имеет смысл унифицировать эти конструкторы и использовать дополнительное перечисляемое значение, чтобы различать их.

Например:

data Terminality = Terminal | Nonterminal
data TrieSet a = Node Terminality (Data.Map a (TrieSet a))

foo :: TrieSet X -> X
foo (Node _ m) = ...

Если вы не хотите вносить это изменение в свой существующий тип данных, вы можете вместо этого определить вспомогательный тип и соответствующую вспомогательную функцию и выполнить преобразование до сопоставления с образцом.

data TreeSetView a = Node Terminality (Data.Map a (TrieSet a))

view :: TrieSet a => TreeSetView a
view (Terminal    m) = TreeSetView TerminalityTerminal    m
view (Nonterminal m) = TreeSetView TerminalityNonterminal m
person Stuart Cook    schedule 20.08.2011
comment
+1, факторизация таких случаев конструктора - очень аккуратное решение, которое, кажется, не используется так часто, как могло бы (еще один бонус заключается в том, что факторизация - это буквально то, что здесь делается). - person C. A. McCann; 21.08.2011
comment
+1 это имеет большой смысл. Мне, например, определенно нужно освоиться с таким рефакторингом типов данных. - person Dan Burton; 21.08.2011