考虑以下方法count
将类型级自然数映射到值级自然数:
{-# LANGUAGE
DataKinds
, KindSignatures
, PolyKinds
, FlexibleInstances
, FlexibleContexts
, ScopedTypeVariables
#-}
module Nat where
data Nat = Z | S Nat
data Proxy (a :: k) = Proxy
class Count a where
count :: a -> Int
instance Count (Proxy Z) where
count _ = 0
instance Count (Proxy n) => Count (Proxy (S n)) where
count _ = succ $ count (Proxy :: Proxy n)
似乎在repl中工作:
λ count (Proxy :: Proxy (S(S(S Z))) )
3
对于终止的递归,在Proxy
的类型的运行时必须有一些指示,但是类型应该在运行时被擦除。我甚至可以用data
定义中的newtype
替换Proxy
:
newtype Proxy (a :: k) = Proxy ()
- 每次都要求它拥有相同的内存表示,因此它是Coercible
。考虑到这一点:
我完全不明白如何调度方法。我会理论化,或者:
因此,如果对象没有以某种方式直接或间接标记其类型,我不明白运行时系统如何确定方法实例的正确选择。周围的人都在谈论一些字典传递的东西,但我完全不明白:
......等等。
即使有一个技巧允许选择方法实例而不用类型标记对象,但只有2个Count
实例,所以方法的选择只能带1一点信息。 (例如,可能有Proxy
标记为“将方法A 1 应用于我”,以及A <中的方法实例sub> 1 使用“将实例A 0 中的方法应用于我”来重置Proxy
。这显然是不够的。运行时必须有一些东西在每次应用递归实例时都会下降。
您能指导我执行此代码,还是引入一些描述运行时系统适用细节的链接?
答案 0 :(得分:7)
每当约束出现在函数声明的LHS时,如
count :: (Count a) => a -> Int
它是
的语法糖count' :: CountDictionary a -> a -> Int
其中CountDictionary a
是适合运行时的(但是单例 - 编译器总是为每种类型选择一个实例!)实际上是{{1}的方法的表示} class,即
Count
在我进一步阐述之前,让我在没有那些丑陋代理的情况下重写所有内容,而不是data CountDictionary a = CountDict {
countMethod :: a -> Int
}
:
TypeApplications
现在当你写{-# LANGUAGE AllowAmbiguousTypes, TypeApplications, ScopedTypeVariables, UnicodeSyntax #-}
class Count a where
count :: Int
⇒ count' :: CountDictionary a -> Int
w/ data CountDictionary a = CountDict Int
instance Count Z where
count = 0
instance ∀ n . Count n => Count (S n) where
count = succ $ count @n
时,它由
count @(S(S(S Z)))
答案 1 :(得分:5)
类型类被去掉了记录。一切都在编译时发生。
data Count a = Count { count :: a -> Int }
instance_Count_ProxyZ :: Count (Proxy Z)
instance_Count_ProxyZ = Count { count = \_ -> 0 }
instance_Count_ProxySn :: Count (Proxy n) -> Count (Proxy (S n))
instance_Count_ProxySn context = Count {
count = \_ -> succ (count context (Proxy :: Proxy n)) }
每当我们调用count :: Count n => n -> Int
时,desberarer(在typechecker之后运行)会查看n
的推断类型,并尝试构造类型为Count n
的记录。
因此,如果我们写count (Proxy :: Proxy (S (S (S Z))))
,我们需要Count (S (S (S Z)))
类型的记录,唯一匹配的实例是Count (Proxy n) -> Count (Proxy (S n))
,n ~ S (S Z)
。这意味着我们现在必须构造其类型为Count (Proxy (S (S Z)))
的参数,依此类推。
请注意,这也是count
实例中Proxy (S n)
的申请过程中发生的情况。
在此过程之后,没有剩下的类型类,一切都只是记录。