如何在Haskell中建模多态列表?

Xav*_*hay 10 haskell

我正在尝试在haskell中建模一些多态类型的数据.我理解为什么以下代码不起作用,但我希望它说明我正在尝试做什么.我的问题是:使用Haskell对此进行建模的惯用方法是什么?(如果有更好的方法,您不需要保持输入格式相同 - 我没有任何现有的代码或数据.)

data Running = Sprint | Jog deriving (Show)
data Lifting = Barbell | Dumbbell deriving (Show)

data Time   = Time   Integer deriving (Show)
data Pounds = Pounds Integer deriving (Show)

data TimedActivity    = TimedActivity Running Time deriving (Show)
data WeightedActivity = WeightedActivity Lifting Pounds deriving (Show)

class Activity a

instance Activity TimedActivity
instance Activity WeightedActivity

-- I have a list of activities
main :: IO ()
main = putStrLn $ show [ TimedActivity Sprint (Time 10)
                       , WeightedActivity Barbell (Pounds 100)
                       ]

-- I then want to apply functions to generate summaries and
-- reports from those activities, i.e.:
extractLifts :: (Activity x) => [x] -> [WeightedActivity]
extractTimes :: (Activity x) => [x] -> [TimedActivity]
Run Code Online (Sandbox Code Playgroud)

Gab*_*lez 8

对于您的特定示例,您可以使用an Either来统一同一列表中的两种类型:

both :: [Either TimedActivity WeightedActivity]
both = [ Left $ TimedActivity Sprint (Time 10)
       , Right $ WeightedActivity Barbell (Pounds 100)
       ]

extractLifts :: [Either TimedActivity WeightedActivity] -> [WeightedActivity]
extractLifts = rights

extractTimes :: [Either TimedActivity WeightedActivity] -> [TimedActivity]
extractTimes = lefts
Run Code Online (Sandbox Code Playgroud)

对于两种以上的类型,只需定义自己的抽象数据类型即可统一它们:

data Multiple = Case1 Type1 | Case2 Type2 | ...
Run Code Online (Sandbox Code Playgroud)

...和提取功能如下:

extractCase1 :: [Multiple] -> [Type1]
extractCase1 ms = [t1 | Case1 t1 <- ms]
Run Code Online (Sandbox Code Playgroud)


bhe*_*ilr 3

简而言之,如果您想要多态列表,请使用 Python。

长的答案是 Haskell 故意设计这样做。有多种方法可以实现完全多态的“列表”,但它们很难使用并且效率较低。您将无法对它们使用普通的列表方法,因为它们不是这种类型[a]。如果你想组合两种数据类型,类型Either非常方便,并且有很多内置函数,但是添加的类型越多,类型签名就会变得越不方便。一个好的经验法则是,如果您尝试构建多态列表,那么您就做错了。Haskell 有很好的方法用代数数据类型封装类型。

此特定代码的问题在于,虽然 和WeightedActivity都是TimedActivity的实例Activity,但列表仍然必须包含 的单个实例Activity。类型列表Activity a => [a]并不是说可以混合不同的活动,而是列表中的所有成员(都是Activitys)都是相同的类型Activity。您不能拥有Ints 和的列表Doubles,因为它们是不同的类型,即使它们都有Num实例。

相反,您可以将TimedActivityWeightedActivity、 和组合Activity成单个数据类型,如下所示

data Activity
    = TimedActivity Running Time
    | WeightedActivity Lifting Pounds
    deriving (Show)
Run Code Online (Sandbox Code Playgroud)

然后,如果您要添加新活动,只需将它们添加到Activity数据类型中即可。那么你的extract函数就很容易通过模式匹配来编写。

  • “简短的回答是,如果你想要多态列表,请使用 Python”……你是认真的吗?) (9认同)
  • @bheklilr:使用“Dynamic”几乎与 Python 方法相同。除此之外,当有人试图学习 Haskell 或学习任何新东西时,不要以“你应该/可以在 xyz 中做到这一点”开始回答。 (3认同)