Сопоставление с образцом, в котором образец основан на параметре (функции) - PullRequest
5 голосов
/ 18 мая 2011

Я хотел бы написать функцию, которая принимает оба

  • конструктор значений для определенного алгебраического типа данных и
  • фактическое значение того же типа,

и определяет, является ли данное значение «созданным из» данного конструктора. Сопоставление с образцом кажется естественным для этого, но сопоставляемый образец должен был бы быть параметром функции, а не жестко заданным именем конструктора.

Код ниже - это то, что я пробовал, но GHC сообщает об ошибке разбора в указанной строке.

Есть ли способ сделать это?

data FooBar = Foo Int | Bar String

-- Imagine that these are useful functions.
processInt :: Int -> String
processInt = show
processString :: String -> String
processString = id

-- This should take one of the above functions and adapt it to operate on
-- FooBar values of compatible "type".  Values that match the given FooBar
-- constructor should be "unwrapped" and passed to the given function.
typeCheck :: (a -> FooBar) -> (a -> String) -> (FooBar -> Maybe String)
typeCheck constructor func fooBar = case fooBar of
  (constructor x) -> Just (func x)  -- GHC says "Parse error in pattern: constructor"
  _ -> Nothing

-- Define processing functions that operate on FooBars.
processFoo :: FooBar -> Maybe String
processFoo = typeCheck Foo processInt
processBar :: FooBar -> Maybe String
processBar = typeCheck Bar processString

Ответы [ 2 ]

3 голосов
/ 18 мая 2011

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

Вы, безусловно, можете сделать это, если можете:

  • перечислять конструкторы типа
  • имеют равенство на элементах типа

Примерно так (я разрываю применение части f, поскольку она ортогональна):

wasBuilt :: Eq t => (t -> Either t t)   -- ^ the constructor
                 -> Either t t          -- ^ a value
                 -> Maybe t             -- ^ the transformed result

wasBuilt k v = case v of
    Left  x | v == k x    -> Just x
    Right x | v == k x    -> Just x
    _                     -> Nothing

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


Вот примерно то, о чем я думал, но обратите внимание, что это продвинутая техника.Явное сопоставление с образцом в обычном AST намного идиоматичнее:

import Generics.SYB

-- only works for unary constructors
sameConstructor :: (Data a, Data b) => (a -> b) -> b -> Bool
sameConstructor k v = toConstr v == toConstr (k undefined)

> sameConstructor (Left :: Char -> Either Char Char) (Right 'x')
False

> sameConstructor (Left :: Char -> Either Char Char) (Left 'x')
True

> sameConstructor (:[]) "haskell"
True
2 голосов
/ 21 мая 2011

Возможно, вы захотите взглянуть на Тип-комбинаторы с безопасным типом функциональная жемчужина. Хотя он не очень хорошо сочетается с синтаксисом сопоставления с образцом в Haskell, он позволяет вам иметь модульность составных шаблонов первого класса, если это то, что вам нужно (то есть, если добавленная компоновка перевешивает синтаксические неудобства).

...