如何将数据元素映射到数据类型

时间:2017-05-19 13:34:23

标签: haskell

我有一个如下所示的数据类型:

data MyAwesomeType = MyAwesomeType {
                                     a :: String,
                                     b :: String,
                                     c :: String,
                                     ...
                                     -- 25 in total
                                   } deriving (Show)

我有一些看起来像这样的数据:

let xs = [["A", "B", "C", ...],["D", "E", "F", ...]]

为类型中的每个字段添加一个值。

如何将自定义数据类型应用于上面列表中的每个元素?我试图做这样的事情,这不起作用:

map (MyAwesomeType) xs

3 个答案:

答案 0 :(得分:6)

您需要定义一个函数,以便从给定类型转换为自定义类型。在上面的示例中,您需要一个函数

myAwesomeTypeConverter :: [String] -> Maybe MyAwesomeType
myAwesomeTypeConverter (a:b:c:[]) = Just $ MyAwesomeType a b c
myAwesomeTypeConverter _ = Nothing

现在您可以使用此功能映射数组

map myAwesomeTypeConverter xs

答案 1 :(得分:4)

三个(或小)个参数

您可以使用 lambda表达式

map (\[a,b,c] -> MyAwesomeType a b c) xs

然而,我们通常不太喜欢使用这样的列表:在编译时,未知列表中有多少元素,因此如果少于或多于3,则会出错。如果您想要一些通用结构来存储值。

你最好使用元组(这里是(String,String,String),因为现在你知道有三个元素,而且元素可以有不同的类型(列表不是这种情况):

let xs = [("A", "B", "C"),("D", "E", "F")]
--        ^    tuple    ^ ^    tuple    ^

然后你可以使用:

map (\(a,b,c) -> MyAwesomeType a b c) xs

使用模板Haskell

的更多参数

如果“参数”的数量很大(即25),那么当然这不会非常优雅。在这种情况下,您可以决定使用模板Haskell ,例如:

module Templates where

import Control.Monad(replicateM)
import Language.Haskell.TH.Syntax(newName,Pat(ListP,VarP),Exp(LamE,VarE,AppE))

listmap n = do
    xs <- replicateM n $ newName "x"
    f <- newName "f"
    return $ LamE [VarP f,ListP (map VarP xs)] $ foldl AppE (VarE f) $ map VarE xs

现在在您的主程序中,您可以使用$(listmap 25)

*Template> :t $(listmap 25)
$(listmap 25)
  :: (t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t1
      -> t)
     -> [t1] -> t

因此,这构造了一个函数,该函数将t1上的25 t映射为一个函数,然后构造一个将t1列表映射到{{1 }}

然后您可以像:

一样使用它
t

如前所述,你最好使用元组,你可以用:

import Templates(listmap)

{-# LANGUAGE TemplateHaskell #-}

map ($(listmap 25) MyAwesomeType) xs
然后可以使用

,例如:

module Templates where

import Control.Monad(replicateM)
import Language.Haskell.TH.Syntax(newName,Pat(TupP,VarP),Exp(LamE,VarE,AppE))

curryN n = do
    xs <- replicateM n $ newName "x"
    f <- newName "f"
    return $ LamE [VarP f,TupP (map VarP xs)] $ foldl AppE (VarE f) $ map VarE xs

答案 2 :(得分:3)

另一种方法,使用generics-sop

{-# language DeriveGeneric #-}
{-# language TypeOperators #-}
{-# language TypeFamilies #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language ScopedTypeVariables #-}

import qualified GHC.Generics as GHC
import Generics.SOP

data MyAwesomeType = MyAwesomeType
                   {
                       a :: String,
                       b :: String,
                       c :: String
                   } deriving (Show,GHC.Generic)

instance Generic MyAwesomeType -- this Generic is from generics-sop

awesomeFromList :: forall c r xs. (Generic r, Code r ~ '[xs], All ((~) c) xs)
                => [c]
                -> Maybe r
awesomeFromList fields =
    to . SOP . Z . hcliftA (Proxy :: Proxy ((~) c)) (mapKI id)
    <$>
    Generics.SOP.fromList fields

适用于实施Generics.SOP.Generic的任何统一记录。

*Main> awesomeFromList ["foo","bar","baz"] :: Maybe MyAwesomeType
Just (MyAwesomeType {a = "foo", b = "bar", c = "baz"})