2013-12-25 3 views
12

Возможно ли создать универсальную функцию, которая будет принимать Foo или Bar в качестве аргумента и вернет функцию, которая использует этот аргумент в своем сопоставлении с образцом?Использовать параметр в качестве шаблона в Haskell

Например, если у меня есть

isFoo :: SomeData -> Bool 
isFoo (Foo _) = True 
isFoo _  = False 

isBar :: SomeData -> Bool 
isBar (Bar _) = True 
isBar _  = False 

Есть ли способ создать обобщенную функцию, что-то вроде

checkType :: SomeClass -> SomeData -> Bool 
checkType (SomeClass _) = True 
checkType _ = False 

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


Фактический код, который я пытаюсь реорганизовать следующая

isString :: [LispVal] -> ThrowsError LispVal 
isString [(String _)] = return $ Bool True 
isString ((String _):xs) = isString xs >>= unpackBool >>= return . Bool 
isString _ = return $ Bool False 

isSymbol :: [LispVal] -> ThrowsError LispVal 
isSymbol [(Atom _)] = return $ Bool True 
isSymbol ((Atom _):xs) = isSymbol xs >>= unpackBool >>= return . Bool 
isSymbol _ = return $ Bool False 

isNumber :: [LispVal] -> ThrowsError LispVal 
isNumber [(Number _)] = return $ Bool True 
isNumber ((Number _):xs) = isNumber xs >>= unpackBool >>= return . Bool 
isNumber _ = return $ Bool False 

Так что я хотел бы какой-нибудь способ, чтобы сделать это более сухой

+1

Может быть, если бы вы рассказать нам немного о том, что вы на самом деле пытаетесь сделать, могло быть лучше. См. [Проблема xy] (http://meta.stackexchange.com/q/66377/169090). Примечание: я новичок в Haskell, поэтому не знаю. – Shahbaz

+0

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

+0

Я думаю, что вы делаете это с помощью шаблона Hsskell. –

ответ

27

Prisms из библиотеки lens могут выступать в качестве «первоклассных узоров». Для того, чтобы определить призм для типа данных:

{-# LANGUAGE TemplateHaskell #-} 

import Control.Lens 

data SomeData = Foo Int 
       | Bar Char 

-- Will create prisms named _Foo and _Bar 
$(makePrisms ''SomeData) 

С Prism s уважительных Fold s, мы можем передать их функции has от Control.Lens.Fold:

*Main> has _Foo (Foo 5) 
True 
*Main> has _Bar (Foo 5) 
False 

Другое интересное применение призм как шаблоны первого класса «переопределяет» поведение функции для случаев, когда аргумент соответствует призме. Вы можете использовать outside от Control.Lens.Prism для этого. outside - это функция, которая принимает Prism и возвращает функции Lens, что позволяет вам «установить» специальный случай. Например:

functionToOverride :: SomeData -> Int 
functionToOverride = const 5 

-- If the arg is a Foo, return the contained int + 1 
newFunction :: SomeData -> Int 
newFunction = functionToOverride & outside _Foo .~ succ 

тестирование обе функции:

*Main> functionToOverride (Foo 77) 
5 
*Main> newFunction (Bar 'a') 
5 
*Main> newFunction (Foo 77) 
78 
1

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

Ближайший обходной путь в настоящее время, вероятно, чтобы обеспечить функцию, которая соответствует по соответствующей схеме:

isString :: [LispVal] -> ThrowsError LispVal 
isString [(String _)] = return $ Bool True 
isString ((String _):xs) = isString xs >>= unpackBool >>= return . Bool 
isString _ = return $ Bool False 

Вы можете заменить верхний матч шаблон с функцией:

isLispVal :: (LispVal -> Bool) -> [LispVal] -> ThrowsError LispVal 
isLispVal p [x] | p x = return $ Bool True 
isLispVal p (x:xs) | p x = isLispVal p xs >>= unpackBool >>= return . Bool 
isLispVal p _ = return $ Bool False 

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

1

кажется, что ваша isString функции просто поднятие функции all.

Рассмотрим это:

data LispVal = Str String | B Bool | Sym String | Num Integer 

isLispStr (Str _) = True 
isLispStr _ = False 

isLispNum (Num _) = True 
isLispNum _ = False 

isLispSym (Sym _) = True 
isLispSym _ = False 

-- etc. for the other LispVal constructors. 

Теперь рассмотрим следующие функции:

isString' :: [LispVal] -> LispVal 
isString' = B . all isLispStr 

isSymbol' :: [LispVal] -> LispVal 
isSymbol' = B . all isLispSym 

-- ... 

Они являются "чистыми" (то есть не-монадические) версии ваших isString и isSymbol функций. монадической версии просто:

isString = return . isString' 
isSymbol = return . isSymbol' 

т.д.

2
{-# LANGUAGE DeriveDataTypeable #-} 
import Data.Data 
import Data.Typeable 

data Foo a = Foo1 a | Foo2 a deriving (Data, Typeable) 

data Bar a = Bar1 a | Bar2 a deriving (Data, Typeable) 

checkType :: Data a => Constr -> a -> Bool 
checkType c v = c == toConstr v 

пример

zeroFoo1 = Foo1 (0 :: Int) 

isFoo1 = checkType (toConstr zeroFoo1) 

обобщать над a ваш checkType, вам необходимо постоянное значение (например, mempty) для каждого конструктора ,

(на самом деле, единственный трюк является toConstr a == toConstr b)

Смежные вопросы