【问题标题】:What types of problems helps "higher-kinded polymorphism" solve better?哪些类型的问题有助于“更高种类的多态性”更好地解决?
【发布时间】:2018-11-09 01:55:16
【问题描述】:

当我阅读History of Haskell 中的一些部分时,我发现:

但是,更高种类的多态性具有独立的效用:完全可以,并且有时非常有用,将数据类型声明为更高种类的参数化,例如:

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

了解“基本”ADT 我在这里有点困惑,我的“猜测”是括号中的部分暗示了一个“参数”/“动态”一元数据构造函数 f?那么任何“可以接受”类型a* -> * 类型的数据构造函数?我的想法是正确的还是我误解了语法?我知道我“只是在猜测”,但我希望在这里获得对这种能力的“外行程序员”直觉,一些需要(或从中受益匪浅)的示例场景;)大多数情况下我可以想象(只是不是在什么确切的方式)这允许在那些“小型嵌入式通用可递归配置语言”中具有更大的灵活性 - Haskell 很高兴为 ...

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

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

所以这似乎是从更清晰的data 声明中“推断”出来的。

【问题讨论】:

    标签: haskell polymorphism higher-kinded-types


    【解决方案1】:

    是的,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 配置设置之后,我们得到了设置始终存在的静态保证。我们现在可以使用 total 访问器 runIdentity 来获取它们,而不是使用危险的 fromJust

    【讨论】:

    • 很好的解释和一个很好的“真实世界”场景来把握这种能力的潜力——非常感谢@chi!
    猜你喜欢
    • 2011-11-28
    • 1970-01-01
    • 1970-01-01
    • 2010-10-16
    • 1970-01-01
    • 1970-01-01
    • 2012-10-14
    • 2011-04-18
    • 1970-01-01
    相关资源
    最近更新 更多