我试图使用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
表示类型相等,但我不了解底层问题是什么,更不用说解决方案或解决方法。
我不理解什么,解决这个问题的最佳方法是什么?
答案 0 :(得分:6)
这里的问题有点微妙。人们可能期望GHC接受这一点的原因是,您拥有所有可能Lower a
的实例,因为您只提供了制作Lower D
和Lower E
的方法。但是,可以构建Lower
的病态定义,如
import GHC.Exts (Any)
data Lower :: Prop1 -> * where
SubThing1 :: Lower D
SubThing2 :: Lower E
SubThing3 :: Lower Any
关键是不仅D
和E
有Prop1
种。我们不仅可以使用像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