我正在处理网络请求,并尝试保存潜在的实体(用户/地址1)。主要问题是,如何将字符串列表转换为函数 - 列表可以是任意长度。
我看过Passing list elements as parameters to curried function,但这只是在事先了解参数数量时才能解决。
data User = User String String
data Address1 = Address1 String
let ufields = ["chris", "str"]
let afields = ["earth"]
我正在寻找一个功能:
f :: [String] -> (? -> c) -> Maybe c
f fields c = undefined
所以我需要传递的是数据构造函数(User / Address1)和字符串列表。
示例:
f ufields User
会返回Just ( User "chris" "str")
。
f ["chris"] User
会返回Nothing
。
f [] User
会返回Nothing
。
f afields Address1
会返回Just ( Address1 "earth" )
。
这可以在不使用TemplateHaskell的情况下完成吗?我可以手动完成上述操作,但它涉及相当多的额外输入:
data User = User String String deriving (Show)
data Address1 = Address1 String deriving (Show)
data EntityType = UserEntity | AddressEntity
data EntityContainer = UserContainer User | AddressContainer Address1
f :: EntityType -> [String] -> Maybe EntityContainer
f UserEntity (p:p':[]) = Just $ UserContainer $ User p p'
f AddressEntity (p:[]) = Just $ AddressContainer $ Address1 p
f _ _ = Nothing
printPossibleEntity :: [String] -> EntityType -> IO ()
printPossibleEntity fields entityType =
case (f entityType fields) of
Just (UserContainer u) -> print u
Just (AddressContainer a) -> print a
Nothing -> print "No entity matched"
main :: IO ()
main = do
let ufields = ["chris", "str"]
let afields = ["earth"]
printPossibleEntity ufields UserEntity
printPossibleEntity afields AddressEntity
printPossibleEntity [] AddressEntity
哪个输出:
User "chris" "str"
Address1 "earth"
"No entity matched"
答案 0 :(得分:4)
让我先说明你几乎肯定不使用它。
执行此类操作的常用方法是使多个参数类型重叠。
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances,
UndecidableInstances #-}
class PackArgs a b | a -> b where
packArgs :: [String] -> a -> Maybe b
instance {-# OVERLAPPING #-} PackArgs y z => PackArgs (String -> y) z where
packArgs [] _ = Nothing
packArgs (a:as) f = packArgs as (f a)
instance {-# OVERLAPPABLE #-} PackArgs z z where
packArgs (_:_) _ = Nothing
packArgs [] z = Just z
这里正在努力:
ghci> data User = User String String deriving Show
ghci> data Address1 = Address1 String deriving Show
ghci> packArgs ["chris","str"] User :: Maybe User
Just (User "chris" "str")
ghci> packArgs ["chris"] User :: Maybe User
Nothing
ghci> packArgs [] User :: Maybe User
Nothing
ghci> packArgs ["earth"] Address1 :: Maybe Address1
Just (Address1 "earth")
问题是我们需要类型注释才能工作。简而言之,Haskell需要知道您期望的返回类型是什么。我们可以通过某些类型系列来解决这个问题。
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances,
UndecidableInstances, TypeFamilies, ScopedTypeVariables #-}
import Data.Proxy
type family StringFuncReturn a where
StringFuncReturn (String -> b) = StringFuncReturn b
StringFuncReturn b = b
class PackArgs a where
packArgs :: [String] -> a -> Maybe (StringFuncReturn a)
instance (StringFuncReturn a ~ r, PackArgs' a r) => PackArgs a where
packArgs = packArgs' (Proxy :: Proxy r)
class PackArgs' a b where
packArgs' :: Proxy b -> [String] -> a -> Maybe b
instance {-# OVERLAPPING #-} PackArgs' y z => PackArgs' (String -> y) z where
packArgs' _ [] _ = Nothing
packArgs' p (a:as) f = packArgs' p as (f a)
instance {-# OVERLAPPABLE #-} PackArgs' z z where
packArgs' _ (_:_) _ = Nothing
packArgs' _ [] z = Just z
这里正在努力:
ghci> data User = User String String deriving Show
ghci> data Address1 = Address1 String deriving Show
ghci> packArgs ["chris","str"] User
Just (User "chris" "str")
ghci> packArgs ["chris"] User
Nothing
ghci> packArgs [] User
Nothing
ghci> packArgs ["earth"] Address1
Just (Address1 "earth")