為什么 Haskell 中的型別必須在型別建構式引數中顯式引數化?
例如:
data Maybe a = Nothing | Just a
這里a必須用型別指定。為什么不能只在建構式中指定?
data Maybe = Nothing | Just a
他們為什么從設計的角度做出這樣的選擇?這個比那個好嗎?
我知道第一個比第二個更強型別,但第二個甚至沒有選項。
編輯 :
示例函式
data Maybe = Just a | Nothing
div :: (Int -> Int -> Maybe)
div a b
| b == 0 = Nothing
| otherwise = Just (a / b)
uj5u.com熱心網友回復:
使用 GADT 表示法可能會使事情變得更清楚,因為標準表示法將型別級和值級語言混合在一起。
標準Maybe型別因此看起來像一個 GADT:
{-# LANGUAGE GADTs #-}
data Maybe a where
Nothing :: Maybe a
Just :: a -> Maybe a
“未引數化”版本也是可能的:
data EMaybe where
ENothing :: EMaybe
EJust :: a -> EMaybe
(正如 Joseph Sible 所評論的,這被稱為存在型別)。現在你可以定義
foo :: Maybe Int
foo = Just 37
foo' :: EMaybe
foo' = EJust 37
太好了,為什么我們不使用EMaybealways 呢?
好吧,問題是當你想使用這樣的值時。隨著Maybe它的精致,你必須包含的型別的完全控制:
bhrar :: Maybe Int -> String
bhrar Nothing = "No number ??"
bhrar (Just i)
| i<0 = "Negative ??"
| otherwise = replicate i '??'
但是你可以用 type 的值做什么EMaybe?事實證明并不多,因為EJust包含某個未知型別的值。因此,無論您嘗試將值用于什么,都會導致型別錯誤,因為編譯器無法確認它實際上是正確的型別。
bhrar :: EMaybe -> String
bhrar' (EJust i) = replicate i '??'
=====> Error couldn't match expected type Int with a
uj5u.com熱心網友回復:
如果一個變數沒有反映在回傳型別中,它被認為是存在的。這是可以定義的,data ExMaybe = ExNothing | forall a. ExJust a但引數 toExJust完全沒用。ExJust True并且ExJust ()兩者都有型別ExMaybe并且從型別系統的角度來看是無法區分的。
這是原始Maybe和存在的 GADT 語法ExMaybe
{-# Language GADTs #-}
{-# Language LambdaCase #-}
{-# Language PolyKinds #-}
{-# Language ScopedTypeVariables #-}
{-# Language StandaloneKindSignatures #-}
{-# Language TypeApplications #-}
import Data.Kind (Type)
import Prelude hiding (Maybe(..))
type Maybe :: Type -> Type
data Maybe a where
Nothing :: Maybe a
Just :: a -> Maybe a
type ExMaybe :: Type
data ExMaybe where
ExNothing :: ExMaybe
ExJust :: a -> ExMaybe
你的問題就像問為什么一個函式f x = ..需要指定它的引數,可以選擇使型別引數不可見,但這很奇怪,但即使不可見,引數仍然存在。
-- >> :t JUST
-- JUST :: a -> MAYBE
-- >> :t JUST 'a'
-- JUST 'a' :: MAYBE
type MAYBE :: forall (a :: Type). Type
data MAYBE where
NOTHING :: MAYBE @a
JUST :: a -> MAYBE @a
mAYBE :: b -> (a -> b) -> MAYBE @a -> b
mAYBE nOTHING jUST = \case
NOTHING -> nOTHING
JUST a -> jUST a
uj5u.com熱心網友回復:
具有顯式型別引數使其更具表現力。沒有它,你會失去很多資訊。例如,您將如何撰寫map? 還是一般的函子?
map :: (a -> b) -> [a] -> [b]
這個版本幾乎沒有說明發生了什么
map :: (a -> b) -> [] -> []
或者更糟的是head:
head :: [] -> a
現在我們突然可以使用不安全強制和零型別安全了。
unsafeCoerce :: a -> b
unsafeCoerce x = head [x]
但我們不僅失去了安全,我們還失去了做一些事情的能力。例如,如果我們想要將read某些內容放入串列 or 中Maybe,我們就無法再指定我們想要的串列型別。
read :: Read a => a
example :: [Int] -> String
main = do
xs <- getLine
putStringLine (example xs)
如果沒有具有顯式型別引數的串列,將無法撰寫此程式。(或者更確切地說,read不能對不同的串列型別有不同的實作,因為內容型別現在是不透明的)
然而,正如其他人所提到的,仍然可以通過使用ExistentialQuantification擴展來定義類似的型別。但是在這些情況下,您在如何使用這些資料型別方面非常有限,因為您無法知道它們包含什么。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/347699.html
上一篇:將Ord函式作為函式引數傳遞
