类型级别的计算何时会与类型族“发生”?

时间:2019-01-21 22:46:45

标签: haskell type-families type-level-computation

我试图就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,即使它们与完全相同的类型相对应。它们是独立计算的吗(减慢了编译速度),还是在类型级别“共享”了计算?

1 个答案:

答案 0 :(得分:2)

Haskell具有“类型擦除语义”。也就是说,假设编译器可以解析所有类型,然后在编译时键入推断“ happens”。在运行时没有计算效果。

编译器可能无法在单独的编译下“解析所有类型”:也就是说,它需要推迟对该模块的推断,直到将其导入的其他模块进行编译为止。在最坏的情况下,这可能需要推迟执行时间。然后执行的是字典传递/字典查找。

解释现代GHC(包括类型族)中类型推断的论文是OutsideIn(X)。您的答案将在那里。

但是,认真的说,为什么要为“计算”类型的性能内部感到烦恼?就是这样。而且您的整个问题都充满期待程序算法的恶臭。而类型求解的行为更像是逻辑编程。问“何时”执行表达式比问“何时”使用惰性语言对表达式求值更不适当。