簡體   English   中英

在Haskell中使用參數作為模式

[英]Use a parameter as pattern in Haskell

是否有可能創建一個泛型函數,將Foo或Bar作為參數,並返回一個在模式匹配中使用該參數的函數?

例如,如果我有

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

所以我想要一些方法讓它更干燥

lens庫中的Prism可以作為“一流的圖案”。 要為您的數據類型定義棱鏡:

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens

data SomeData = Foo Int 
              | Bar Char

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

由於Prism是有效的Fold ,我們可以將它們傳遞給Control.Lens.Foldhas函數:

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

棱鏡作為第一類模式的另一個有趣的應用是“覆蓋”函數的行為,用於參數與棱鏡匹配的情況。 您可以outside Control.Lens.Prism使用outside來執行此操作。 outside是一個函數,它接受Prism並返回一個Lens for functions,它允許你“設置”特殊情況。 例如:

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
{-# 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)

概括在acheckType ,你需要一個恆定值(例如mempty每個構造函數)。

(真的,唯一的技巧是toConstr a == toConstr b

目前這是不可能的,雖然一些允許它的擴展正在進行中。

目前最接近的解決方法可能是提供與適當模式匹配的函數:

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

當我這樣做時,我經常最終需要適當的鏡頭而不僅僅是謂詞函數,但這取決於用例。

看來你的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

-- ...

這些是isStringisSymbol函數的“純”(即非monadic)版本。 monadic版本只是:

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

等等

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM