什么类型的问题有助于“更高级别的多态”更好地解决?

时间:2017-02-21 14:44:20

标签: haskell polymorphism higher-kinded-types

当我阅读History of Haskell中的某些部分时,我遇到了:

  

然而,更高级别的多态性具有独立的效用:声明在更高种类上参数化的数据类型是完全可能的,有时非常有用,例如:

data ListFunctor f a = Nil | Cons a (f a)

知道“基本”ADT我在这里有点疑惑,我的“猜测”是parens中的部分表示“参数化”/“动态”一元数据构造函数 f?那么任何类型* -> *的数据构造函数都可以“接受”类型a?我的想法是正确的还是我误解了语法?我知道我“只是在猜测”,但我希望在这里能够获得这种能力的“平等程序员”直觉,一些需要(或从中受益匪浅)的示例场景;)大多数我可以想象(只是没有在什么确切的方式)这使得那些“小型嵌入式多功能可递送配置语言”-ADT更加灵活,Haskell很高兴能为{close}制定和编写evals

在GHCi中,上面的:i ListFunctor给出了:

type role ListFunctor representational nominal
data ListFunctor (f :: * -> *) a = Nil | Cons a (f a)

所以这似乎是来自更加清晰的data声明的“推断”。

1 个答案:

答案 0 :(得分:14)

是的,f可以是任何一元类型的构造函数。

例如,ListFunctor [] IntListFunctor Maybe Char是良好的。

f也可以是部分应用了(n-1)个参数的任何n元型构造函数。

例如,ListFunctor ((->) Bool) IntListFunctor (Either ()) Char是良好的。

基本的绑定系统非常简单。如果F :: * -> * -> ... -> *,则F需要类型参数。如果G :: (* -> *) -> *,则G期望任何类型* -> *包括一元类型构造函数和部分应用程序,如上所示。等等。

通过更高类型很好地解决的问题是配置选项。假设我们有一个记录

data Opt = Opt 
   { opt1 :: Bool
   , opt2 :: String
   -- many other fields here
   }

现在,配置设置可以在文件中找到和/或通过命令行和/或环境变量传递。在解析所有这些设置源的过程中,我们需要应对并非所有源都定义所有选项的事实。因此,我们需要一个更宽松的类型来表示配置设置的子集:

data TempOpt = TempOpt 
   { tempOpt1 :: Maybe Bool
   , tempOpt2 :: Maybe String
   -- many other fields here
   }

-- merge all options in one single configuration, or fail
finalize :: [TempOpt] -> Maybe Opt
...

这太可怕了,因为它复制了所有选项!我们很想删除Opt类型,只使用较弱的TempOpt来减少混乱。但是,通过这样做,我们需要在每次需要访问程序中的选项值时使用一些部分访问器,例如fromJust,即使在初始配置处理部分之后也是如此。

我们可以采用更高级别的方式:

data FOpt f = FOpt 
   { opt1 :: f Bool
   , opt2 :: f String
   -- many other fields here
   }
type Opt = FOpt Identity
type TempOpt = FOpt Maybe

-- as before: merge all options in one single configuration, or fail
finalize :: [TempOpt] -> Maybe Opt
...

不再重复。在我们finalize配置设置之后,我们获得了始终存在设置的静态保证。我们现在可以使用总计访问者runIdentity来获取它们,而不是危险的fromJust