嵌套类型级编程

时间:2016-12-25 07:17:41

标签: haskell data-kinds

我试图使用DataKinds进行类型级编程,但是当我将这些结构中的一个嵌套在另一个结构中时遇到了困难。

{-# LANGUAGE DataKinds, TypeFamilies, GADTs, MultiParamTypeClasses, FlexibleInstances #-}

module Temp where

data Prop1 = D | E 

data Lower :: Prop1 -> * where
  SubThing1 :: Lower D
  SubThing2 :: Lower E

class ClassLower a where
  somefunc2 :: a -> String

instance ClassLower (Lower D) where
  somefunc2 a = "string3"

instance ClassLower (Lower E) where
  somefunc2 a = "string4"

data Prop2 = A | B | C

data Upper :: Prop2 -> * where
  Thing1 :: Upper A
  Thing2 :: Upper B
  Thing3 :: Lower a -> Upper C

class ClassUpper a where
  somefunc :: a -> String

instance ClassUpper (Upper A) where
  somefunc a = "string1"

instance ClassUpper (Upper B) where
  somefunc a = "string2"

instance ClassUpper (Upper C) where
  somefunc (Thing3 x) = somefunc2 x

一旦我添加了ClassUpper的最后一个实例,我就会出错。

Temp.hs:37:25: error:
    • Could not deduce (ClassLower (Lower a))
        arising from a use of ‘somefunc2’
      from the context: 'C ~ 'C
        bound by a pattern with constructor:
                   Thing3 :: forall (a :: Prop1). Lower a -> Upper 'C,
                 in an equation for ‘somefunc’
        at /Users/jdouglas/jeff/emulator/src/Temp.hs:37:13-20
    • In the expression: somefunc2 x
      In an equation for ‘somefunc’: somefunc (Thing3 x) = somefunc2 x
      In the instance declaration for ‘ClassUpper (Upper 'C)’

我理解'C ~ 'C表示类型相等,但我不了解底层问题是什么,更不用说解决方案或解决方法。

我不理解什么,解决这个问题的最佳方法是什么?

2 个答案:

答案 0 :(得分:6)

这里的问题有点微妙。人们可能期望GHC接受这一点的原因是,您拥有所有可能Lower a的实例,因为您只提供了制作Lower DLower E的方法。但是,可以构建Lower的病态定义,如

import GHC.Exts (Any)

data Lower :: Prop1 -> * where
  SubThing1 :: Lower D
  SubThing2 :: Lower E
  SubThing3 :: Lower Any

关键是不仅DEProp1种。我们不仅可以使用像Any这样的东西来玩这样的恶作剧 - 即使允许使用以下构造函数(所以F Int :: Prop1也是如此)!

  SubThing4 :: Lower (F Int)

type family F x :: Prop1 where {}

因此,总而言之,基本问题是GHC确实无法确定ClassLower (Lower a)约束(由于使用somefunc2而需要)将会得到满足。要做到这一点,它必须做一些工作检查GADT构造函数,并确保某些实例涵盖每个可能的情况。

在这种情况下,您可以通过将ClassLower (Lower a)约束添加到GADT构造函数(启用FlexibleContexts)来解决您的问题。

data Upper :: Prop2 -> * where
  Thing1 :: Upper A
  Thing2 :: Upper B
  Thing3 :: ClassLower (Lower a) => Lower a -> Upper C

答案 1 :(得分:3)

或者您可以像这样写出package spittr.web; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping({"/","/homepage"}) public class HomeController { @RequestMapping(method=RequestMethod.GET) public String home(){ return "home"; } } 个实例,使用模式匹配(而不是类型变量)来区分GADT的情况:

ClassLower