根据类型获取Haskell记录字段

时间:2019-02-08 09:56:27

标签: haskell typeclass

我想定义一个类型类,用于根据记录的类型访问记录中的特定字段。在这个玩具示例中,我们有一个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的可能性。

1 个答案:

答案 0 :(得分:0)

  

用于根据记录中的特定字段访问它们的类型类   输入

可以使用generic programming完成这种操作,DeriveGeneric是一种检查数据类型的结构并根据每个数据结构定义可在各种数据类型上工作的功能的技术。

要使用通用编程,必须启用GHC.Generics扩展名并导入Data.Generics.Product.Typed模块。

{-# 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软件包的typed模块中实现。该模块提供了lens镜头,可让我们根据记录的(唯一)类型来定位记录中的字段。

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

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

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

在这里,由于类型签名,编译器推断出我们需要Money字段。但是我们也可以使用显式的apk.tools

{-# LANGUAGE TypeApplications #-}

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