Haskell / GHC: сопоставление нескольких унарных конструкторов с одинаковым шаблоном - PullRequest
4 голосов
/ 20 августа 2011

Итак, я играл с определением типа данных 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. Существует ли прагма ЯЗЫКА, которую я могу дать GHC, чтобы она принимала их?

Ответы [ 2 ]

9 голосов
/ 20 августа 2011
  1. Да. Подстановочные знаки такого типа никогда не поддерживаются.
  2. По моему мнению, было бы намного сложнее определить тип функции, если вы не знаете конструктор данных, с которым были сопоставлены. Просто подумайте о функции f (_ n) = n. Каким должен быть его тип? Система типов в Haskell не может описать арность конструкторов типа, поэтому такая функция, как f, не может существовать.
  3. Я так не думаю.
8 голосов
/ 20 августа 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
...