Haskell DSL中的常量和表达式都可以有一个类型类吗?

Haskell DSL中的常量和表达式都可以有一个类型类吗?,haskell,dsl,functional-dependencies,type-families,Haskell,Dsl,Functional Dependencies,Type Families,假设我有一个DSL,用LangL r a进行计算。我可能想让函数同时使用常量0::Int、lala::String和DSL表达式LangL r a。因此,我实现了一个类型类。然而,无论我如何尝试实现它,我都会遇到问题 下面是使用类型族时出现问题的一个最小示例: {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleInstances #-} {-

假设我有一个DSL,用LangL r a进行计算。我可能想让函数同时使用常量0::Int、lala::String和DSL表达式LangL r a。因此,我实现了一个类型类。然而,无论我如何尝试实现它,我都会遇到问题

下面是使用类型族时出现问题的一个最小示例:

{-# LANGUAGE DeriveAnyClass        #-}
{-# LANGUAGE DeriveFunctor         #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE StandaloneDeriving    #-}
{-# LANGUAGE TypeFamilies          #-}

data LangL r a = LangL a
deriving instance Functor (LangL r)
deriving instance Applicative (LangL r)

class DSLEntity r a where
  type ValueOf a
  entityValue :: a -> LangL r (ValueOf a)

instance DSLEntity r (LangL r a) where
  type ValueOf (LangL r a) = a
  entityValue = id

instance DSLEntity r Int where
  type ValueOf Int = Int
  entityValue = pure

foo :: LangL r Int -> LangL r Int
foo m = entityValue (entityValue m)
GHC给出以下输出:

    • Ambiguous type variable ‘r0’ arising from a use of ‘entityValue’
      prevents the constraint ‘(DSLEntity
                                  r (LangL r0 Int))’ from being solved.
      Relevant bindings include
        m :: LangL r Int (bound at temp.hs:25:5)
        foo :: LangL r Int -> LangL r Int
          (bound at temp.hs:25:1)
      Probable fix: use a type annotation to specify what ‘r0’ should be.
      These potential instance exist:
        instance DSLEntity r (LangL r a)
          -- Defined at temp.hs:16:10
    • In the expression: entityValue (entityValue m)
      In an equation for ‘foo’: foo m = entityValue (entityValue m)
   |
temp.hs:25:22-34: error: …
    • Ambiguous type variable ‘r0’ arising from a use of ‘entityValue’
      prevents the constraint ‘(DSLEntity
                                  r0 (LangL r Int))’ from being solved.
      Relevant bindings include
        m :: LangL r Int (bound at temp.hs:25:5)
        foo :: LangL r Int -> LangL r Int
          (bound at temp.hs:25:1)
      Probable fix: use a type annotation to specify what ‘r0’ should be.
      These potential instance exist:
        instance DSLEntity r (LangL r a)
          -- Defined at /temp.hs:16:10
    • In the first argument of ‘entityValue’, namely ‘(entityValue m)’
      In the expression: entityValue (entityValue m)
      In an equation for ‘foo’: foo m = entityValue (entityValue m)
   |
问题很清楚。LangL r a的r参数和DSL实体的r参数之间没有依赖关系。然而,我们不能添加这样的依赖项,因为对于Int实例来说,它实际上并不存在


我很困惑,想知道是否有可能完成我想做的事情。如果没有,原因是什么?

我认为您只需要在中间类型方面给GHC一些帮助:

{-# LANGUAGE ScopedTypeVariables   #-}

foo :: forall r. LangL r Int -> LangL r Int
foo m = entityValue (entityValue m :: LangL r Int)

我认为您只需要在中间类型方面给GHC一些帮助:

{-# LANGUAGE ScopedTypeVariables   #-}

foo :: forall r. LangL r Int -> LangL r Int
foo m = entityValue (entityValue m :: LangL r Int)
您可以使用:

instance (r ~ r') => DSLEntity r' (LangL r a) where
而不是:

instance DSLEntity r (LangL r a) where
这实际上有点神秘

您最初的实例声明说,GHC只能在能够证明LangL r a中的r在参数和entityValue的结果中是相同类型时使用该实例。但是entityValue::a->LangL r Valuer是a的,因此任何类型都可以用作输入,并要求GHC查找匹配的实例。特别是,任何langlr0a都可以显示为输入,即使对于不匹配的r也是如此。因此,在entityValue entityValue m中,第一个可以在任何r0中使用,第二个可以将其转换回foo类型中使用的r。因为GHC不能建立中间的R,所以您会遇到模糊的类型变量阻止它知道应该选择哪个DSLIt实体实例来解决约束的问题。 而实例r~r'=>DSLR'LangL r a表示该实例适用于任何类型的r和r',但使用它增加了r和r'相等的约束。这听起来和编写实例DSLEntity r LangL R一样,但实际上不是因为GHC在选择实例时不考虑约束,而是在事后才考虑约束。现在GHC不需要证明r和r'相等就可以选择这个实例,它会在DSLEntity约束的第二个参数看起来像LangL uu________________________________________________;否则会出现类型错误

通过查看entityValue的类型,可以非常清楚地看到差异。entityValue。使用原始实例,您可以获得:

λ :t entityValue . entityValue 
entityValue . entityValue
  :: (DSLEntity r1 (LangL r2 (ValueOf a)), DSLEntity r2 a) =>
     a -> LangL r1 (ValueOf a)
通过新实例,您可以得到以下结果:

λ :t entityValue . entityValue 
entityValue . entityValue
  :: DSLEntity r a => a -> LangL r (ValueOf a)
您可以使用:

instance (r ~ r') => DSLEntity r' (LangL r a) where
而不是:

instance DSLEntity r (LangL r a) where
这实际上有点神秘

您最初的实例声明说,GHC只能在能够证明LangL r a中的r在参数和entityValue的结果中是相同类型时使用该实例。但是entityValue::a->LangL r Valuer是a的,因此任何类型都可以用作输入,并要求GHC查找匹配的实例。特别是,任何langlr0a都可以显示为输入,即使对于不匹配的r也是如此。因此,在entityValue entityValue m中,第一个可以在任何r0中使用,第二个可以将其转换回foo类型中使用的r。因为GHC不能建立中间的R,所以您会遇到模糊的类型变量阻止它知道应该选择哪个DSLIt实体实例来解决约束的问题。 而实例r~r'=>DSLR'LangL r a表示该实例适用于任何类型的r和r',但使用它增加了r和r'相等的约束。这听起来和编写实例DSLEntity r LangL R一样,但实际上不是因为GHC在选择实例时不考虑约束,而是在事后才考虑约束。现在GHC不需要证明r和r'相等就可以选择这个实例,它会在DSLEntity约束的第二个参数看起来像LangL uu________________________________________________;否则会出现类型错误

通过查看entityValue的类型,可以非常清楚地看到差异。entityValue。使用原始实例,您可以获得:

λ :t entityValue . entityValue 
entityValue . entityValue
  :: (DSLEntity r1 (LangL r2 (ValueOf a)), DSLEntity r2 a) =>
     a -> LangL r1 (ValueOf a)
通过新实例,您可以得到以下结果:

λ :t entityValue . entityValue 
entityValue . entityValue
  :: DSLEntity r a => a -> LangL r (ValueOf a)

r是什么意思?你为什么需要它?@n.“代词是m。DSL需要它。确切地说,它是可变变量的引用类型。可能是IORef/TVar/etc。总有人可以添加实例DSL实体Foo LangL Bar a,而您对此无能为力。所以依赖关系并不存在,不仅对Int,对LangL r a也是如此。@n.“代词m”。是的,没有。然而,我希望它是。这段代码是我尝试过的证明,正如本网站所要求的那样
. 尽管如此,我还是想知道一种方法,即使它与lotBen回答得相当好,但我认为整个问题闻起来有点像XY问题。你的DSL类型的Num或者IsString实例会给你带来你需要的便利吗?r的含义是什么?你为什么需要它?@n.“代词是m。DSL需要它。确切地说,它是可变变量的引用类型。可能是IORef/TVar/etc。总有人可以添加实例DSL实体Foo LangL Bar a,而您对此无能为力。所以依赖关系并不存在,不仅对Int,对LangL r a也是如此。@n.“代词m”。是的,没有。然而,我希望它是。这段代码是我尝试过的证明,正如本网站所要求的那样。尽管如此,我还是想知道一种方法,即使它与lotBen回答得相当好,但我认为整个问题闻起来有点像XY问题。您的DSL类型的Num和IsString实例是否能为您提供所需的便利?非常感谢!你详细的回答有助于理解发生了什么。非常感谢!你详细的回答有助于理解发生了什么。