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