簡體   English   中英

參數多態與更高類型的類型有什么區別?

[英]What's the difference between parametric polymorphism and higher-kinded types?

我很確定他們不一樣。 但是,我對“ Rust不支持”更高種類的類型(HKT)而是提供參數多態性這一普遍觀念感到困惑。 我試圖繞開它並理解它們之間的區別,但是卻越來越糾結。

據我了解, 魯斯特高kinded類型,至少是基本。 使用“ *”符號,HKT確實具有例如* -> * 例如, Maybe* -> *的一種,可以在Haskell中這樣實現。

data Maybe a = Just a | Nothing

這里,

  • Maybe是類型構造函數,需要將其應用於具體類型以成為類型“ *”的具體類型。
  • Just aNothing是數據構造函數。

在有關Haskell的教科書中,這通常被用作高種類型的示例。 但是,在Rust中,它可以簡單地實現為枚舉,它畢竟是sum類型

enum Maybe<T> {
    Just(T),
    Nothing,
}

區別在哪里? 以我的理解,這是一種類型較高的很好的例子。

  1. 如果在Haskell中將其用作HKT的教科書示例,為什么會說Rust沒有HKT? Maybe枚舉不符合HKT的資格嗎?
  2. 應該說Rust沒有完全支持HKT嗎?
  3. HKT和參數多態性之間的根本區別是什么?

在查看函數時,這種困惑仍在繼續,我可以編寫一個采用Maybe的參數函數,並據我所知將HKT作為函數參數。

fn do_something<T>(input: Maybe<T>) {
    // implementation
}

同樣,在Haskell中,

do_something :: Maybe a -> ()
do_something :: Maybe a -> ()
do_something _ = ()

這導致了第四個問題。

  1. 對更高種類的類型的支持到底在哪里結束? 使Rust的類型系統無法表達HKT的最小示例是什么?

相關問題:

我遇到了許多與該主題相關的問題(包括它們與博客文章的鏈接等),但找不到主要問題的答案(1和2)。

  1. 在Haskell中,“高級類型”是否真的是類型? 還是僅表示* concrete *類型的集合而已?
  2. 沒有類型參數的泛型類型的泛型結構
  3. Scala中的高級類型
  4. 哪些類型的問題可以幫助“更高種類的多態性”更好地解決?
  5. Haskell中的抽象數據類型與參數多態性

更新資料

感謝您提供的許多很好的答案,這些答案非常詳細,對您有很大幫助。 我決定接受安德里亞斯·羅斯伯格(Andreas Rossberg)的回答,因為他的解釋為我提供了最大的幫助,使其走上正確的道路。 特別是有關術語的部分。

我真的陷入了那種以為一切* -> * ... -> *都是更高種類的想法的循環中。 強調* -> * -> *(* -> *) -> *之間差異的解釋對我來說至關重要。

一些術語:

  • 那種*有時被稱為地面 您可以將其視為0階。
  • * -> * -> ... -> *至少帶有一個箭頭的任何形式都是一階的
  • 高階種類是具有“左側的嵌套箭頭”的種類,例如(* -> *) -> *

順序本質上是箭頭左側嵌套的深度,例如(* -> *) -> *是二階的, ((* -> *) -> *) -> *是三階的,依此類推(FWIW,相同的概念也適用於類型本身:二階函數是其類型具有例如(A -> B) -> C形式的函數(A -> B) -> C

非地面類型的類型(順序> 0)也稱為類型構造函數 (某些文獻僅將地面類型的類型稱為“類型”)。 較高種類的類型(構造函數)是其種類較高的類型(順序> 1)。

因此,一種類型較高的類型是接受非地面類型的參數的類型。 這將需要非地面類型的類型變量,許多語言不支持這種類型的變量。 Haskell中的示例:

type Ground = Int
type FirstOrder a = Maybe a  -- a is ground
type SecondOrder c = c Int   -- c is a first-order constructor
type ThirdOrder c = c Maybe  -- c is second-order

后兩個種類更高。

同樣, 種類較多的多態性描述了(參數)多態性值的存在,這些值在非基礎類型上抽象。 同樣,很少有語言支持這一點。 例:

f : forall c. c Int -> c Int  -- c is a constructor

聲明Rust支持“代替”更高種類類型的參數多態是沒有道理的。 兩者都是相互補充的參數化的不同維度。 當您將兩者結合在一起時,您將擁有更高種類的多態性。

Rust不能做的一個簡單示例就是Haskell的Functor類。

class Functor f where
    fmap :: (a -> b) -> f a -> f b

-- a couple examples:
instance Functor Maybe where
    -- fmap :: (a -> b) -> Maybe a -> Maybe b
    fmap _ Nothing  = Nothing
    fmap f (Just x) = Just (f x)

instance Functor [] where
    -- fmap :: (a -> b) -> [a] -> [b]
    fmap _ []     = []
    fmap f (x:xs) = f x : fmap f xs

請注意,實例是在類型構造函數Maybe[] ,而不是在完全應用的類型Maybe a[a]

這不僅僅是一個客廳的把戲。 它與參數多態性有很強的交互作用。 由於fmap類型中的類型變量ab不受類定義的約束,因此Functor實例無法基於它們更改其行為。 在從類型進行代碼推理時,這是一個不可思議的強大特性,而Haskell的類型系統的優勢在很大程度上來自於此。

它還有另一個屬性-您可以在類型較高的類型變量中編寫抽象的代碼。 這是幾個例子:

focusFirst :: Functor f => (a -> f b) -> (a, c) -> f (b, c)
focusFirst f (a, c) = fmap (\x -> (x, c)) (f a)

focusSecond :: Functor f => (a -> f b) -> (c, a) -> f (c, b)
focusSecond f (c, a) = fmap (\x -> (c, x)) (f a)

我承認,這些類型開始看起來像抽象的廢話。 但是,當您有幾個使用高級抽象的助手時,它們實際上是非常實用的。

newtype Identity a = Identity { runIdentity :: a }

instance Functor Identity where
    -- fmap :: (a -> b) -> Identity a -> Identity b
    fmap f (Identity x) = Identity (f x)

newtype Const c b = Const { getConst :: c }

instance Functor (Const c) where
    -- fmap :: (a -> b) -> Const c a -> Const c b
    fmap _ (Const c) = Const c

set :: ((a -> Identity b) -> s -> Identity t) -> b -> s -> t
set f b s = runIdentity (f (\_ -> Identity b) s)

get :: ((a -> Const a b) -> s -> Const a t) -> s -> a
get f s = getConst (f (\x -> Const x) s)

(如果我在那里犯了任何錯誤,那么有人可以解決這些錯誤嗎?我正在重新實現lens最基本起點,而無需編譯器。)

可以將函數focusFirstfocusSecond作為第一個參數傳遞給getset ,因為它們類型中的類型變量f可以與getset更具體的類型統一。 能夠抽象出類型較高的類型變量f可以將特定形狀的函數用作任意數據類型中的設置器和獲取器。 這是導致lens庫的兩個核心見解之一。 沒有這種抽象就不可能存在。

(就其價值而言,另一個關鍵的見解是將鏡片定義為類似的功能,從而使鏡片的構成成為簡單的功能構成。)

因此,不僅僅可以接受類型變量。 重要的部分是能夠使用與類型構造函數相對應的類型變量,而不是某些具體的(如果未知)類型。

參數多態性只是指函數不能在其定義中使用類型(或種類)的任何特定特征的性質。 這是一個完整的黑匣子。 標准示例是length :: [a] -> Int ,它僅適用於列表的結構 ,不適用於列表中存儲的特定值。

HKT的標准示例是Functor類,其中fmap :: (a -> b) -> fa -> fb length不同, a具有*f具有* -> * fmap 表現出參數多態性,因為fmap不能在其定義中使用ab的任何屬性。

fmap表現出特殊的多態性,因為該定義可以針對為其定義的特定類型構造函數f進行定制。 也就是說,有單獨的定義fmapf ~ [] f ~ Maybe ,等不同的是, f是“申報”為類型類定義的一部分,而不是僅僅是定義的一部分fmap (實際上,添加了類型類以支持某種程度的即席多態性。沒有類型類,則存在參數多態性。您可以編寫一個函數來支持一個具體類型或任何具體類型,但不支持兩者之間的某些較小的集合。)

我將繼續它:種類繁多的類型只是類型級別的高階函數。

但是花一點時間:

考慮一下monad變壓器:

newtype StateT s m a :: * -> (* -> *) -> * -> *

這里,

- s is the desired type of the state
- m is a functor, another monad that StateT will wrap
- a is the return type of an expression of type StateT s m

什么是上等類型?

m :: (* -> *)

因為采用類型*並返回類型*

就像類型上的函數,即類型的類型構造函數

* -> *

在Java之類的語言中,您不能

class ClassExample<T, a> {
    T<a> function()
}

在Haskell T中,類型為*->* ,但是Java類型(即類)不能具有該類型的類型參數,即類型較高的類型。

另外,如果您不知道,在基本的Haskell中,表達式必須具有帶有kind *的類型,即“具體類型”。 任何其他類型,例如* -> *

例如,您不能創建Maybe類型的表達式。 它必須是應用於諸如Maybe IntMaybe String等之類的參數的類型。換句話說,就是完全應用的類型構造函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM