在Haskell中将参数用作模式
是否可以生成一个通用函数,该函数将接受Foo或Bar作为参数,并返回一个在其模式匹配中使用该参数的函数 例如,如果我有在Haskell中将参数用作模式,haskell,pattern-matching,Haskell,Pattern Matching,是否可以生成一个通用函数,该函数将接受Foo或Bar作为参数,并返回一个在其模式匹配中使用该参数的函数 例如,如果我有 isFoo :: SomeData -> Bool isFoo (Foo _) = True isFoo _ = False isBar :: SomeData -> Bool isBar (Bar _) = True isBar _ = False 有没有办法创建一个泛型函数,比如 checkType :: SomeClass ->
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
所以我想用一些方法使它更干燥,目前这是不可能的,尽管一些允许它的扩展正在进行中 目前最接近的解决方法可能是提供与适当模式匹配的功能:
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
当我这样做的时候,我常常需要合适的镜头,而不仅仅是谓词函数,但这取决于用例。库中的可用作“一流模式”。要为数据类型定义棱柱,请执行以下操作:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data SomeData = Foo Int
| Bar Char
-- Will create prisms named _Foo and _Bar
$(makePrisms ''SomeData)
由于Prism
s是有效的Fold
s,我们可以将它们从Control.Lens.Fold
传递到函数:
*Main> has _Foo (Foo 5)
True
*Main> has _Bar (Foo 5)
False
prisms作为一级模式的另一个有趣的应用是在参数与prism匹配的情况下“重写”函数的行为。您可以使用from来完成此操作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
isString' :: [LispVal] -> LispVal
isString' = B . all isLispStr
isSymbol' :: [LispVal] -> LispVal
isSymbol' = B . all isLispSym
-- ...
您的
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.
现在考虑这些函数:
*Main> functionToOverride (Foo 77)
5
*Main> newFunction (Bar 'a')
5
*Main> newFunction (Foo 77)
78
isString' :: [LispVal] -> LispVal
isString' = B . all isLispStr
isSymbol' :: [LispVal] -> LispVal
isSymbol' = B . all isLispSym
-- ...
这些是isString
和isSymbol
函数的“纯”(即非一元)版本。
一元版本只是:
isString = return . isString'
isSymbol = return . isSymbol'
等等
范例
zeroFoo1 = Foo1 (0 :: Int)
isFoo1 = checkType (toConstr zeroFoo1)
要在a
上泛化您的checkType
,您需要为每个构造函数提供一个常量值(例如mempty
)
(实际上,唯一的诀窍是
toConstr a==toConstr b
)你的意思是是否有一种通用的方法来检查值是否是用特定的构造函数构造的?不,我不这么认为。事实上,我相当肯定这是不可能的。也许,如果你描述一下你的用例,我们可以想出一种方法来做你想做的事情?也许,如果你告诉我们一些你实际上在做什么,可能会有更好的方法。看见注意:我是Haskell的初学者,所以我不太清楚。Haskell有很多泛型编程库,可能允许这种情况。我想你可以用模板Hsskell来实现。另外,对于泛型的实例,构造函数名可以作为字符串使用,并且可以派生。不过,我不确定你是否能自动得出更有效的结果。