scala - 更高种类的类型什么时候有用?

标签 scala haskell types f# higher-kinded-types

我用 F# 进行开发已经有一段时间了,我喜欢它。然而,据我所知,F# 中不存在一个流行词,即高级类型。我读过有关高等类型的 Material ,我想我理解他们的定义。我只是不确定它们为什么有用。有人可以提供一些示例,说明哪些高级类型在 Scala 或 Haskell 中变得容易,但需要在 F# 中解决方法吗?同样对于这些示例,如果没有更高种类的类型(或者在 F# 中反之亦然),解决方法是什么?也许我只是太习惯解决它,以至于没有注意到该功能的缺失。

(我认为)我明白,而不是 myList |> List.map fmyList |> Seq.map f |> Seq.toList 更高种类的类型允许你只需编写myList |> map f,它就会返回一个List。这很好(假设它是正确的),但看起来有点小? (难道不能简单地通过允许函数重载来完成吗?)我通常会转换为 Seq ,然后我可以转换为我想要的任何内容。再说一次,也许我太习惯解决这个问题了。但是有没有任何例子可以证明高级类型真正可以节省您的击键次数或类型安全性?

最佳答案

所以类型的种类就是它的简单类型。例如,Int 具有 * 类型,这意味着它是基本类型,可以通过值实例化。通过对高级类型的一些宽松定义(我不确定 F# 的界限在哪里,所以我们只包含它)多态容器是高级类型的一个很好的例子。 p>

data List a = Cons a (List a) | Nil

类型构造函数 List 具有 * -> * 类型,这意味着必须向其传递具体类型才能生成具体类型:List Int 可以拥有像 [1,2,3] 这样的居民,但 List 本身不能。

我假设多态容器的好处是显而易见的,但除了容器之外,还存在更有用的 * -> * 类型。例如,关系

data Rel a = Rel (a -> a -> Bool)

或解析器

data Parser a = Parser (String -> [(a, String)])

两者也都有类型* -> *

<小时/>

然而,我们可以在 Haskell 中更进一步,通过使用具有更高阶类型的类型。例如,我们可以查找类型为 (* -> *) -> * 的类型。一个简单的例子是 Shape,它尝试填充 * -> * 类型的容器。

data Shape f = Shape (f ())

Shape [(), (), ()] :: Shape []

例如,这对于在 Haskell 中表征 Traversable 非常有用,因为它们始终可以分为其形状和内容。

split :: Traversable t => t a -> (Shape t, [a])
<小时/>

作为另一个例子,让我们考虑一棵根据其分支类型进行参数化的树。例如,一棵普通的树可能是

data Tree a = Branch (Tree a) a (Tree a) | Leaf

但是我们可以看到分支类型包含Tree aPair,因此我们可以以参数方式从类型中提取该部分

data TreeG f a = Branch a (f (TreeG f a)) | Leaf

data Pair a = Pair a a
type Tree a = TreeG Pair a

TreeG 类型构造函数具有类型 (* -> *) -> * -> *。我们可以用它来制作有趣的其他变体,例如 RoseTree

type RoseTree a = TreeG [] a

rose :: RoseTree Int
rose = Branch 3 [Branch 2 [Leaf, Leaf], Leaf, Branch 4 [Branch 4 []]]

或者病态的,比如MaybeTree

data Empty a = Empty
type MaybeTree a = TreeG Empty a

nothing :: MaybeTree a
nothing = Leaf

just :: a -> MaybeTree a
just a = Branch a Empty

或者一个TreeTree

type TreeTree a = TreeG Tree a

treetree :: TreeTree Int
treetree = Branch 3 (Branch Leaf (Pair Leaf Leaf))
<小时/>

这个出现的另一个地方是“仿函数代数”。如果我们放弃几层抽象,这可能会更好地被视为折叠,例如 sum::[Int] -> Int。代数通过仿函数载体进行参数化。 仿函数具有类型* -> *和载体类型*,所以总共

data Alg f a = Alg (f a -> a)

有种类(* -> *) -> * -> *Alg 很有用,因为它与数据类型和在其之上构建的递归方案有关。

-- | The "single-layer of an expression" functor has kind `(* -> *)`
data ExpF x = Lit Int
            | Add x x
            | Sub x x
            | Mult x x

-- | The fixed point of a functor has kind `(* -> *) -> *`
data Fix f = Fix (f (Fix f))

type Exp = Fix ExpF

exp :: Exp
exp = Fix (Add (Fix (Lit 3)) (Fix (Lit 4))) -- 3 + 4

fold :: Functor f => Alg f a -> Fix f -> a
fold (Alg phi) (Fix f) = phi (fmap (fold (Alg phi)) f)
<小时/>

最后,尽管它们在理论上是可能的,但我从未见过甚至更高级的类型构造函数。我们有时会看到这种类型的函数,例如 mask::((forall a.IO a -> IO a) -> IO b) -> IO b,但我认为你必须挖掘进入类型序言或依赖类型文献以了解类型的复杂程度。

关于scala - 更高种类的类型什么时候有用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21170493/

相关文章:

java - 在 Spark Java 应用程序中使用 Tuple2 时出现编译错误

javascript - document.write 在 html 中不起作用

haskell - 如何模拟货币、货币和在货币之间交换货币的银行?

haskell - 用数字输入歧义

JavaScript请解释一下这个语法

types - Idris 中是否存在宇宙不一致的重要示例?

scala - 使用带有 bintray-sbt 插件的 sbt 时如何发布快照?

scala - 如何加入 2 个 spark sql 流

haskell - cabal 安装 yesod 失败?

haskell - cabal-install 配置文件的文档