haskell - 有没有办法在 GHC 的编译时打开约束字典?
问题描述
考虑以下 Haskell 代码(GHC 8.2):
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
import Data.Constraint
type family Head xs where
Head (x ': xs) = x
type family Tail xs where
Tail (x ': xs) = xs
class IsList xs where
isList :: (xs ~ '[] => r) -> ((xs ~ (Head xs ': Tail xs), IsList (Tail xs)) => r) -> r
instance IsList '[] where isList r _ = r
instance IsList xs => IsList (x ': xs) where isList _ r = r
type family Prepend xs ys where
Prepend '[] ys = ys
Prepend (x ': xs) ys = x ': Prepend xs ys
prependPreservesIsList :: forall xs ys. (IsList xs, IsList ys) => Dict (IsList (Prepend xs ys))
prependPreservesIsList = isList @xs Dict (withDict (prependPreservesIsList @(Tail xs) @ys) Dict)
class IsList (Deps a) => Hard (a :: *) where
type Deps a :: [*]
instance (Hard a, Hard b) => Hard (Either a b) where
type Deps (Either a b) = Prepend (Deps a) (Deps b)
它失败了
Main.hs:37:10: error:
• Could not deduce (IsList (Prepend (Deps a) (Deps b)))
arising from the superclasses of an instance declaration
from the context: (Hard a, Hard b)
bound by the instance declaration at Main.hs:37:10-46
• In the instance declaration for ‘Hard (Either a b)’
|
37 | instance (Hard a, Hard b) => Hard (Either a b) where
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
代码试图做的是构造一个Hard
具有关联类型列表的类Deps
,其中Deps
对应的Either a b
应该是Deps
对应的a
和的串联b
。
我们知道如何向 GHC 证明这种形式的连接保留了IsList
类,如prependPreservesIsList
. 如果我们有(Hard a, Hard b)
并且我们需要编写需要(IsList (Deps (Either a b)))
我们的普通代码,那么我们就withDict prependPreservesIsList
在路上。但是我们需要 GHC 在“编译时”识别这个约束,以确保Either a b
实例是合法的。
有没有办法在编译时“打开约束字典”,或者以其他方式修改此代码以使 GHC 接受Either a b
实例?
解决方案
考虑从类型级列表切换到类型级树。所以:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
data Tree a = Empty | Node a | Branch (Tree a) (Tree a)
class IsTree xs where
isTree ::
(xs ~ 'Empty => a) ->
(forall x. xs ~ 'Node x => a) ->
(forall l r. (xs ~ 'Branch l r, IsTree l, IsTree r) => a) ->
a
instance IsTree 'Empty where isTree a _ _ = a
instance IsTree ('Node x) where isTree _ a _ = a
instance (IsTree l, IsTree r) => IsTree ('Branch l r) where isTree _ _ a = a
class IsTree (Deps a) => Hard a where
type Deps a :: Tree *
instance (Hard a, Hard b) => Hard (Either a b) where
type Deps (Either a b) = 'Branch (Deps a) (Deps b)
推荐阅读
- docker - 在 Github Actions 上从 package.json 安装私有 github 包
- javascript - jQuery选择字符串上的第二个元素
- python - 迭代地平均特定列表元素?
- python-3.x - 为什么这个函数被认为是队列的实现?
- javascript - 对 JavaScript 数组中的相同值进行分组
- axapta - Dynamics 365 FO 数据实体:临时表为空
- intel - 如何从 MPSS 4.4.1 修复 modprobe 错误
- reactjs - 懒惰渲染孩子
- batch-file - How to output multiple lines from a FINDSTR to a variable
- cordova - Fabric.js + IonicV3 - 'touch:gesture' 事件