为封闭类型族确定的类型编写类型类实例时出现问题

时间:2018-02-08 07:22:22

标签: haskell

我尝试使用类型文字,封闭类型系列和一堆不同的东西来建模我自己的枚举类型。 (我知道GHC.Generics可能是一种更好的方法,但现在我想知道,发生了什么。)

通过反复试验(我不得不承认)我终于到达了这个程序,编译并且有些东西正在工作(基本情况),但递归不是:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilyDependencies #-}


module Wft2 where

import GHC.TypeLits
import GHC.Exts

data Label (l :: Symbol) = Value deriving Eq

instance (KnownSymbol l) => Show (Label (l :: Symbol)) where show = symbolVal

type family Enumerate (a :: [Symbol]) = b | b -> a where
  Enumerate '[] = () 
  Enumerate '[a] = Label a
  Enumerate (a ': b) = Either (Label a) (Enumerate b)

type family CheckEmbedImpl (a :: [Symbol]) (orig :: [Symbol]) (b :: Symbol) where
  CheckEmbedImpl '[] orig b = TypeError (ShowType b :<>: Text " not contained in " :<>: ShowType orig)
  CheckEmbedImpl (b : rest) orig b = (() :: Constraint)
  CheckEmbedImpl (b : rest) orig c = CheckEmbedImpl rest orig c

type family CheckEmbed (a :: [Symbol]) (b :: Symbol) where
  CheckEmbed a b = CheckEmbedImpl a a b

class Embed (l :: [Symbol]) (s :: Symbol) where
  embed :: (CheckEmbed l s) => Label s -> Enumerate l

instance {-# OVERLAPS #-} (Enumerate '[t] ~ Label t) => Embed '[t] t where embed _ = Value
instance {-# OVERLAPS #-} Embed '[t] s where embed = undefined
instance {-# OVERLAPS #-} (Either (Label a) (Enumerate b) ~ Enumerate (a ': b)) =>
  Embed (a ': b) a where embed _ = Left Value
instance {-# OVERLAPS #-} (Either (Label a) (Enumerate b) ~ Enumerate (a ': b),
                           Embed b t,
                           CheckEmbed b t) =>
  Embed (a ': b) t where embed l = Right (embed l)

做像embed (Value :: Label "abc") :: Enumerate '["abc"])embed (Value :: Label "abc") :: Enumerate '["abcd"])按预期工作,但embed (Value :: Label "abc") :: Enumerate '["abc", abc2"]给了我错误,如

*Wft2> embed (Value :: Label "abc") :: Enumerate '["abc", "abcd" ]

<interactive>:13:1: error:
    • Couldn't match type ‘Enumerate l0’
                     with ‘Either (Label "abc") (Label "abcd")’
      Expected type: Enumerate '["abc", "abcd"]
        Actual type: Enumerate l0
      The type variable ‘l0’ is ambiguous
    • In the expression:
          embed (Value :: Label "abc") :: Enumerate '["abc", "abcd"]
      In an equation for ‘it’:
          it = embed (Value :: Label "abc") :: Enumerate '["abc", "abcd"]

我认为我的第三个Embed实例上的类型相等约束应该处理这个问题。为什么GHC不能推断l0 ~ '["abc", "abcd"]因此 Enumerate l0 ~ Either (Label "abc") (Label "abcd")

奖励积分:如果您了解一些教程,尝试做类似的事情并有一些示例,请提供指示。

要注意它是一个内射型家庭,所以不应该责怪类型家庭不是单射的。

1 个答案:

答案 0 :(得分:1)

内射型家庭相当新,不是很进化,而且相当多。

请注意,在以下模式中:

Enumerate '[a] = ...
Enumerate (a ': b) = ...

如果b ~ '[]那么这些模式都匹配,两者之间的选择是模糊的。 (另请注意,虽然b '[] Enumerate '["abc", "abcd" ]中的b不是'[]显而易见,但编译器并不是先验地知道这一点 - 这正是它所尝试的证明使用注入性。)

此类型系列仍然通过了注入性检查,因为类型检查器知道它是一个封闭类型系列,并且知道Enumerate l ~ Label "x"不能是l ~ "x"或前一个模式匹配。但是,类型家庭的实际评估语义根本不关心类型家庭是开放还是封闭。注入性根本不影响对类型家族的评估;它只允许类型检查器将Enumerate (a ': (b ': c)) = Either (Label a) (Enumerate (b ': c)) 之类的约束减少到def find(self,v): if self.value is None: return False if self.value==v: return (True) if v<self.value and self.left is not None: return (self.left.find(v)) if v>self.value and self.right is not None: return (self.right.find(v)) return False

解决方案是将最后一个模式更改为

Active Directory Sites and Services > Sites > Default-First-Site-Name > Servers > MyDCName > NTDS Settings

这使得它显而易见&#39;这不能与前一个重叠。