Могу ли я сопоставить шаблон конструктора данных в Haskell?

У меня

data Foo = X (String, Int) | A String | B String | C String | D String -- ...

и определили

f (X (s, _)) =s 
f (A s) = s
f (B s) = s
f (C s) = s
f (D s) = s
-- ...

но предпочтет написать что-то вроде

f (X (s, _)) =s 
f (_ s) = s

Но, похоже, нет способа сделать это (я получаю "синтаксическую ошибку", связанную с _).

Есть ли способ сопоставления шаблона конструктора данных в Haskell?

Ответ 1

Неа. Но вы можете написать это:

data Foo
    = X { name :: String, age :: Int } 
    | A { name :: String }
    | B { name :: String }
    | C { name :: String }
    | D { name :: String }

а затем name :: Foo -> String. Вы могли бы также рассмотреть это:

data Tag = A | B | C | D
data Foo = X String Int | Tagged Tag String

f (X s _) = s
f (Tagged _ s) = s

Ответ 2

В дополнение к ответу @DanielWagner альтернативой является использование Отменить ваш шаблон (AKA "SYB" ). Он позволяет вам найти первый подтерм данного типа. Таким образом, вы можете определить

{-# LANGUAGE DeriveDataTypeable #-}

import Control.Monad
import Data.Data
import Data.Generics.Schemes
import Data.Typeable

data Foo = X (String, Int) | A String | B String | C String | D String
  deriving (Show, Eq, Ord, Data, Typeable)

fooString :: Foo -> Maybe String
fooString = msum . gmapQ cast

и fooString вернут первый аргумент String ваших конструкторов. Функция cast отфильтровывает String и gmapQ получает отфильтрованные значения для всех непосредственных субтерм.

Однако это не вернет String из X, потому что X не имеет непосредственного подтерминала String, он имеет только один подтерм типа (String, Int). Чтобы получить первый String в любом месте иерархии терминов, вы можете использовать everywhere:

fooString' :: Foo -> Maybe String
fooString' = everything mplus cast

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