【问题标题】:Getting Haskell record fields based on their type根据类型获取 Haskell 记录字段
【发布时间】:2019-07-02 13:03:22
【问题描述】:

我想定义一个类型类,用于根据类型访问记录中的特定字段。在这个玩具示例中,我们有一个 Failable(这只是一个 Either),它可以出现在不同的记录中并包装不同的类型。我很感兴趣是否可以定义一个函数 failableFrom 并让编译器根据上下文选择正确的实例。

type Money  = Double
type Name   = String
type ErrMsg = String

class HasFailable a b where
  failableFrom :: a -> Either ErrMsg b

data SomeRecord = SomeRecord (Either ErrMsg Name) (Either ErrMsg Money)

instance HasFailable SomeRecord Name where
  failableFrom (SomeRecord name _) = name

instance HasFailable SomeRecord Money where
  failableFrom (SomeRecord _ money) = money

data SomeOtherRecord = SomeOtherRecord (Either ErrMsg Name)

instance HasFailable SomeOtherRecord Name where
  failableFrom (SomeOtherRecord name) = name

data SomeOtherOtherRecord = SomeOtherOtherRecord (Either ErrMsg Money)

instance HasFailable SomeOtherOtherRecord Money where
  failableFrom (SomeOtherOtherRecord money) = money



-- some record
record = SomeRecord (Right "John") (Right 200.0)

-- let the compiler decide what failableFrom function to use
moreMoney = fmap (\money -> money + 200.0) $ failableFrom record

我问这个主要是出于对 Haskell 中可能发生的事情的好奇。

【问题讨论】:

  • 如果您将最后一个 instance HasFailable SomeOtherOtherRecord Name 更改为 instance HasFailable SomeOtherOtherRecord Money 并确定 moreMoney 的类型(例如通过执行 money + 200 :: Money),则该代码有效。
  • SomeRecord, SomeOtherRecordSomeOtherOtherRecord 都可以是我认为的类型。我认为你正在做的事情太难了。
  • @melpomene 已修复,谢谢。

标签: haskell typeclass


【解决方案1】:

一个类型类,用于根据记录访问特定字段 输入

这样的事情可以使用generic programming 来完成,这是一种检查数据类型结构并根据每种数据类型的结构定义跨不同数据类型工作的函数的技术。

要使用泛型编程,必须启用DeriveGeneric 扩展并导入GHC.Generics 模块。

{-# LANGUAGE DeriveGeneric #-}

import GHC.Generics

type Money  = Double
type Name   = String
type ErrMsg = String

data SomeRecord = SomeRecord (Either ErrMsg Name) (Either ErrMsg Money) deriving (Generic)

自己编写基于泛型的“类型化访问器”函数会很复杂。幸运的是,该功能已经在generic-lens 包的Data.Generics.Product.Typed 模块中实现。该模块提供了typed 镜头,可让我们按(唯一)类型定位记录中的字段。

(lens 是一个将记录字段的 getter 和 setter 打包在一起的值。lens 包包含使用它们的主要定义和函数,特别是 @987654328 @函数用于获取字段的值。)

import Control.Lens (view)
import Data.Generics.Product.Typed (typed)

moreMoney :: Either ErrMsg Money
moreMoney = fmap (\money -> money + 200.0) $ view typed record

这里编译器推断出我们需要Money 字段,因为类型签名。但我们也可以使用明确的type application

{-# LANGUAGE TypeApplications #-}

moreMoney' = fmap (\money -> money + 200.0) $ view (typed @(Either _ Money)) record

【讨论】:

  • 使用类型类还允许函数中的参数约束。这里是否有类似的可能,即有一个函数可以接受任何包含 Either ErrMsg Money 的东西?
  • @PetrasPurlys 是的,使用来自Data.Generics.Product.TypedHasType 类型类。 typed这个镜头其实是HasType的一种方法。
猜你喜欢
  • 2012-01-30
  • 1970-01-01
  • 2012-01-29
  • 1970-01-01
  • 2021-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多