【问题标题】:Accessing functions for custom Haskell data types访问自定义 Haskell 数据类型的函数
【发布时间】:2014-08-05 14:06:43
【问题描述】:

我已经搜索并搜索了以下内容,特别是堆栈溢出和一般的 Google。如果它已经被覆盖或微不足道以至于在任何地方都没有提及,我深表歉意。

我为有时是离散的,有时是连续的对象定义了一个自定义数据类型,如下所示:

data Gene =  Discrete String Int | Continuous String Double 
                 deriving (Eq, Show, Read, Ord)

这里,String 代表基因的名称(例如 vWF 或类似的),数字参数是它的状态,离散或连续,如下所示:

bober = Discrete "vWF" 2
slick = Continuous "gurg" 0.45432

我可以使用记录语法来访问基因的属性,但是对于名称和状态有 2 个不同的函数。我想要的是一种访问身份的功能,以及一种访问状态的功能。对于身份,这很简单,因为它是两个值构造函数的字符串:

geneName :: Gene -> String
geneName (Discrete dName _) = dName
geneName (Continuous cName _) = cName

当我尝试创建一个返回基因状态的函数时,我遇到了麻烦。我认为模式匹配会起作用:

geneState :: Num a => Gene -> a
geneState (Discrete _ dState) = dState
geneState (Continuous _ cState) = cState

这在 GHCi 中加载失败,给出:

DynamicalModularity.hs:34:35: 无法匹配类型 Int' withDouble' 预期类型:a 实际类型:双人 在表达式中:cState 在“geneState”的方程中: 基因状态(连续_cState)= cState 失败,已加载模块:无。

我尝试使用case 语法:

geneState :: Num a => Gene -> a
geneState gene = case gene of (Discrete _ dState) -> dState
                              (Continuous _ cState) -> cState

这再次无法加载:

DynamicalModularity.hs:30:56: 无法匹配类型 Int' withDouble' 预期类型:a 实际类型:双人 在表达式中:cState 在另一种情况下:(Continuous _ cState)-> cState 在表达式中: {的案例基因 (离散_dState)-> dState (连续_cState)-> cState } 失败,已加载模块:无。

我的问题是:我想做的事情是否可能和/或好的 Haskell?我错过了一些明显的东西吗?我一直在寻找解决方案一段时间。任何帮助将不胜感激。

【问题讨论】:

  • 您对使用geneState 有何预期?
  • GHC Haskell(existentials)中有这样的东西,但我想在推荐它们之前更好地理解这个问题。
  • 返回一个存在量化的Num 实例是非常没用的。您可以.. 致电 negateabssignum。那里没有任何有价值的东西。
  • @bheklilr Either Int DoubleGene 之间几乎没有区别(只是一个字符串),所以......为什么不直接返回您的输入? :P
  • 如果你认为 Bifunctors 是初学者的答案,我会更积极地提出存在主义 :-)

标签: haskell types


【解决方案1】:

任何会消耗调用geneState 的结果的代码都需要能够同时处理IntDouble——显然是这种情况,因为我可以在离散值和连续值上调用geneState

让我们表示这段代码中分别使用IntDouble 的部分。这两个部分都必须是简单的函数,所以我们可以把它们写成

intConsumer    :: Int    -> result
doubleConsumer :: Double -> result

现在我让这两个部分返回相同的结果,因为消费代码必须始终返回相同类型的东西,无论它接收的是离散的还是连续的Gene

现在,我们可以使用这些信息和模式匹配来编写geneState

geneState :: (Int -> result) -> (Double -> result) -> Gene -> result
geneState intConsumer doubleConsumer (Discrete   _ st) = intConsumer    st
geneState intConsumer doubleConsumer (Continuous _ st) = doubleConsumer st

我们可以简化这个函数的一种方法是假设我们所有的消费者都单独在Doubles 上运行——即使输入Gene 是离散的。这在数学上是合理的,因为Double 包含所有Integers(如果你愿意,可以更正式)。

在 Haskell 中,函数 fromIntegralInts 转换为其他数字类型。我们可以这样写

geneStateAsDouble :: Gene -> Double
geneStateAsDouble = geneState fromIntegral id

【讨论】:

  • 值得注意的是,这只是Either 的教会代表和一张从GeneEither 的遗忘地图。
  • 谢谢。你的回答让我更多地思考什么会消耗这个函数的结果。就是代码后面的建模在连续情况下会完全不同,所以消费者也会产生不同的东西,这意味着我可能应该为每个人设置不同的访问器函数,甚至放弃连续和离散的想法基因完全属于同一类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多