【问题标题】:Using type families and Generics to find an Id value使用类型族和泛型查找 Id 值
【发布时间】:2017-12-05 12:40:34
【问题描述】:

这个问题与this one 有关,我想避免从数据结构中提取Id 值的样板,但以类型安全的方式。

这里我再重复一下问题的相关细节:假设你有一个类型Id

newtype Id = Id { _id :: Int }

并且您想定义一个函数getId,从包含至少一个Id 值的任何结构中提取此Id

class Identifiable e where
    getId :: e -> Id

现在的问题是如何以类型安全的方式定义这样一个类,同时使用泛型避免样板

在我的previous question 中,有人指出了类型族,尤其是described in this blog post 的想法。据我了解,这个想法是定义一个类型类MkIdentifiable,这样:

class MakeIdentifiable (res :: Res) e where
    mkGetId :: Proxy res -> e -> Id

只有在其中嵌套了至少一个 Id 值时,一个值的类型为 Res

data Crumbs = Here | L Crumbs | R Crumbs
data Res = Found Crumbs | NotFound

那么,似乎可以定义:

instance MakeIdentifiable (Found e) e => Identifiable e where
    getId = mkGetId (Proxy :: Proxy (Found e))

现在的问题是如何为Res 定义与 GHC.Generics 的类型(U1K1:*::+:)相关联的类型族。

我尝试了以下方法:

type family HasId e :: Res where
    HasId Id = Found Here
    HasId ((l :+: r) p) = Choose (HasId (l p)) (HasId (r p))

Choose 与上述博文中的定义类似:

type family Choose e f :: Res where
    Choose (Found a) b = Found (L1 a)
    Choose a (Found b) = Found (R1 b)
    Choose a b = NotFound

但这不会编译,因为HasId (l p) 有类型Res,而是需要一个类型。

【问题讨论】:

  • 什么是Choose
  • 我添加了Choose 可能是什么的描述。

标签: haskell type-families ghc-generics


【解决方案1】:

您已经非常接近进行Choose 类型检查了。 L1R1(:+:) 的构造函数,而不是 Crumbs。还有一个 GHC.Generics.R :: * 类型,它在类型级别从 Crumbs 隐藏了 R 构造函数,但您可以使用 'R 来消除歧义(单引号是构造函数,双引号是类型构造函数)。

注释类型也是一种很好的做法,就像我们注释顶级函数的类型一样。

type family Choose (e :: Res) (f :: Res) :: Res where
  Choose (Found a) b = Found ('L a)
  Choose a (Found b) = Found ('R b)
  Choose NotFound NotFound = NotFound  -- I'm not a fan of overlapping families

【讨论】:

    猜你喜欢
    • 2017-12-16
    • 1970-01-01
    • 1970-01-01
    • 2021-12-06
    • 2016-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-02
    相关资源
    最近更新 更多