什么类型的问题有助于“更高主观的多态性”解决得更好? [英] What types of problems helps "higher-kinded polymorphism" solve better?

查看:125
本文介绍了什么类型的问题有助于“更高主观的多态性”解决得更好?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我阅读 Haskell的历史,我碰到过:


然而,更高主观的多态性具有独立的效用:完全有可能,偶尔非常有用,可以声明数据类型参数化为更高类型,如:



  data ListFunctor fa = Nil | (fa)

了解基本ADT我在这里有点困惑,我的猜测是parens中的部分提出了参数化/动态一元数据构造函数 f ?因此,任何类型的数据构造函数 * - > * 可以接受类型 a ?我的想法是正确的还是我误解了语法?我知道我只是在猜测,但我希望在这里能够获得这种能力的外行程序员直觉,一些示例场景需要(或者从中受益);大多数情况下我可以想象(不是在什么准确的方式),这就允许在那些小型嵌入式多用途可递归配置语言中具有更大的灵活性--AST Haskell很高兴为<.. c $ c> evals 编写和编写... close。

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

 类型角色ListFunctor表示名义
数据ListFunctor(f :: * - > *)a = Nil |缺点(fa)

所以这似乎是从$ c $ c> data 声明。

解决方案

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


$ b

例如 ListFunctor [] Int ListFunctor也许Char

例如 ListFunctor(( - >)Bool)Int ListFunctor(Either())Char 都很好。



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




一个很好解决的问题就是配置选项。假设我们有一个记录

 数据选项=选项
{opt1 :: Bool
,opt2 ::字符串
- 其他许多字段
}

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

$ p $ data TempOpt = TempOpt
{tempOpt1: :可能Bool
,tempOpt2 :: Maybe字符串
- 其他许多字段在这里
}

- 在一个单一配置中合并所有选项,或者失败
finalize :: [TempOpt] - >也许Opt
...

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

我们可以采用更高的类型:

  data FOpt f = FOpt 
{opt1 :: f Bool
,opt2 :: f字符串
- 许多其他字段在这里
}
类型Opt = FOpt身份
类型TempOpt = FOpt也许

- 与之前一样:将所有选项合并到一个配置中,或者失败
finalize :: [TempOpt] - >可能是Opt
...

没有更多的重复。在我们 finalize 配置设置后,我们得到静态的保证,即设置始终存在。我们现在可以使用 total 访问器 runIdentity 来获取它们,而不是危险的 fromJust


As I read through some sections in History of Haskell, I came across:

However, higher-kinded polymorphism has independent utility: it is entirely possible, and occasionally very useful, to declare data types parameterised over higher kinds, such as:

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

Knowing "basic" ADTs I was a bit puzzled here, my "guess" was that the part in parens suggests a "parametric"/"dynamic" unary data constructor f? So any data constructor of kind * -> * that "can accept" type a? Is my thinking correct or am I misinterpreting the syntax? I know I'm "just guessing" but I'm hopeful to gain a "lay-programmer" intuition on this capability here, some sample scenario needing (or benefiting immensively from) this ;) mostly I can imagine (just not in what exact manner) this allowing more flexibility in those "small embedded versatile recursable config language"-ADTs that Haskell makes such a pleasure to formulate and write evals for.. close?

In GHCi, :i ListFunctor on the above gives:

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

So this seems to be what's "inferred" from the crisper data declaration.

解决方案

Yes, f can be any unary type constructor.

For instance ListFunctor [] Int or ListFunctor Maybe Char are well-kinded.

f can also be any n-ary type constructor with (n-1) arguments partially applied.

For instance ListFunctor ((->) Bool) Int or ListFunctor (Either ()) Char are well-kinded.

The basic kinding system is quite simple. If F :: * -> * -> ... -> *, then F expects type arguments. If G :: (* -> *) -> *, then G expects any thing of kind * -> * including unary type constructor and partial applications as the ones shown above. And so on.


A problem which is nicely solved by higher kinds is configuration options. Assume we have a record

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

Now, configuration settings can be found in a file and/or passed through the command line and/or in environment variables. During the parsing of all these settings sources, we need to cope with the fact that not all sources define all options. Hence, we need a more lax type to represents subsets of configuration settings:

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
...

This is horrible, since it duplicates all the options! We would be tempted to remove the Opt type, and only use the weaker TempOpt, to reduce clutter. However, by doing this we will need to use some partial accessor like fromJust every time we need to access the value of an option in our program, even after the initial configuration handling part.

We can instead resort to higher kinds:

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
...

No more duplication. After we finalize the configuration settings, we get the static guarantee that settings are always present. We can now use the total accessor runIdentity to get them, instead of the dangerous fromJust.

这篇关于什么类型的问题有助于“更高主观的多态性”解决得更好?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆