如何将值列表转换为数据构造函数的参数?

时间:2018-01-01 15:55:08

标签: haskell

我正在处理网络请求,并尝试保存潜在的实体(用户/地址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"

1 个答案:

答案 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")