我试图就Haskell中使用类型族“发生”的类型级别计算何时(以及多少次)形成一种直觉。举一个具体的例子,考虑使用此类型类,以使用类型级自然值将其索引到n-ary product中:
<View style={styles.component}>
<View style={styles.topSection} />
<View style={styles.bottomSection} />
</View>
我的直觉是,对{-# LANGUAGE DataKinds, TypeOperators, KindSignatures, TypeFamilies, MultiParamTypeClasses,
ScopedTypeVariables, TypeApplications, AllowAmbiguousTypes
#-}
import Data.Kind (Type)
import Data.SOP (I(..),NP(..)) -- identity functor and n-ary product from "sop-core"
data N = Zero | Succ N -- to be used as a kind
class Key (i :: N) (ts :: [Type]) where
type Value i ts :: Type
getValue :: NP I ts -> Value i ts
instance Key Zero (t:ts) where
type Value Zero (t:ts) = t
getValue (I v :* _) = v
instance Key n ts => Key (Succ n) (t : ts) where
type Value (Succ n) (t:ts) = Value n ts
getValue (_ :* rest) = getValue @n rest
getValue' :: forall n ts. Key n ts => NP I ts -> Value n ts
getValue' = getValue @n @ts
getTwoValues :: forall n ts. Key n ts => NP I ts -> NP I ts -> (Value n ts, Value n ts)
getTwoValues np1 np2 = let getty = getValue @n @ts in (getty np1, getty np2)
main :: IO ()
main = do let np = I True :* I 'c' :* Nil
print $ getValue @(Succ Zero) np
print $ getValue' @(Succ Zero) np
print $ getTwoValues @(Succ Zero) np np
中getValue
的出现进行类型检查会触发在编译时搜索相应值类型main
的类型级别列表的“遍历”。对于大型列表而言,这种遍历可能会代价高昂。
但是Value (Succ Zero) '[Bool,Char]
呢?它是否像以前一样触发类型级别列表的“遍历”一次,或者两次 次,一次检查getValue'
本身,另一次检查getValue'
取决于?
那getValue
又如何呢?在其签名中,有两个类型族调用getTwoValues
,即使它们与完全相同的类型相对应。它们是独立计算的吗(减慢了编译速度),还是在类型级别“共享”了计算?
答案 0 :(得分:2)
Haskell具有“类型擦除语义”。也就是说,假设编译器可以解析所有类型,然后在编译时键入推断“ happens”。在运行时没有计算效果。
编译器可能无法在单独的编译下“解析所有类型”:也就是说,它需要推迟对该模块的推断,直到将其导入的其他模块进行编译为止。在最坏的情况下,这可能需要推迟执行时间。然后执行的是字典传递/字典查找。
解释现代GHC(包括类型族)中类型推断的论文是OutsideIn(X)。您的答案将在那里。
但是,认真的说,为什么要为“计算”类型的性能内部感到烦恼?就是这样。而且您的整个问题都充满期待程序算法的恶臭。而类型求解的行为更像是逻辑编程。问“何时”执行表达式比问“何时”使用惰性语言对表达式求值更不适当。