我想要
- 創建一個表示
nonelikeNothingof的新型別和值Maybe。 a為所有(正常值)創建一個新的聯合型別和none- 創建一個接受新聯合型別的函式,然后如果引數是
none什么都不做。
如果不使用Maybe,我怎么能在 Haskell 中做到這一點?
在打字稿中
const none = { undefined };
type None = typeof none;
type Option<T> = None | T;
type f = <T>(a:Option<T>) => any;
const f:f = a =>
a === none
? undefined
: console.log(a);
如何在 Haskell 中做到這一點?以防萬一,請注意這里我不是在問如何使用MaybeMonad。謝謝。
uj5u.com熱心網友回復:
如果你有一個實際的編程問題要嘗試解決,它需要你表示“要么是 T 型別的值,要么什么都沒有”的概念,那么 Haskell 對此的回答是使用Maybe T.
無論您的代碼目標是什么,如果在 Typescript 中您將使用您的Option來實作該目標,那么在 Haskell 中您可以使用它Maybe來實作該目標。代碼會有些不同,但這并不奇怪,因為它們是不同的語言。
但是,如果您試圖直接使用您Option在 Haskell 中的 Typescript 中涉及的概念來實作一個完全一樣的型別(而不是作為解決問題的工具),那么您就完蛋了運氣。Haskell 沒有(無區別的)聯合型別的概念,也沒有輕松模擬一個1的方法。
您可以嘗試使用Typeable:
import Data.Typeable (Typeable, cast)
data None = None
f :: (Typeable a, Show a) => a -> IO ()
f x = case (cast x :: Maybe None)
of Just None -> pure ()
Nothing -> print x
當我們有一個Typeable約束在起作用時,我們實際上可以檢查一個值是否屬于某種型別(如果是,則在參考具有正確型別的地方獲取對它的參考,因此我們可以將其用作該型別)。但這迫使我們Maybe無論如何都要通過,因為cast需要一種方法來表示沒有成功的演員表的價值!
它也很棘手,因為現在我們型別的實際引數None根本無法表示,而Maybe選項可以Just Nothing很好地表示。確實,您并不經常需要這種“嵌套的失敗或缺失”功能(盡管決不是永遠不需要;想想可能不會回傳結果的查詢:如果您需要區分運行查詢失敗與查詢沒有回傳結果,這很自然Maybe (Maybe Result))。但我更喜歡處理任何型別的設施,以統一處理任何型別。如果設計上沒有任何極端情況,你就不會被奇怪的極端情況所吸引。
而且你不能Typeable更一般地使用來實際宣告一個可能是兩種特定型別聯合的型別;您必須接受a帶有Typeable約束的型別變數,這意味著人們可以將任何型別傳遞給您,而不僅僅是您試圖放入聯合中的兩個。由于您只能撰寫有限數量的casts,Typeable因此基本上只能用于專門處理一組特定型別,或用于其他任何東西的代碼路徑(可能會出錯)。
作為更一般的觀點,您需要避免過度使用Typeable才能撰寫獲得 Haskell 型別系統的所有好處的代碼。許多 Haskell 代碼利用了多型型別無法實作的屬性,因為您不知道它是哪種具體型別。Typeable所有這些都在播放時從窗外消失。舉一個非常簡單的例子,有一個經典的論點,即只有一個型別的函式a -> a(不會觸底);由于無法知道型別a是什么,函式的實作不能無中生有,所以它只能不加修改地回傳它。但是一個函式型別Typeable a => a -> a可以有任意數量的奇怪規則編碼,比如“如果引數是一個Integer, add one to it”。無論實作中隱藏了什么,它都不會反映在超出Typeable約束的型別中,所以你必須熟悉實作的細節才能使用這個函式,編譯器贏了如果你犯了錯誤,就不會發現。這種效果的一個明顯例子是,在我f上面寫的嘗試中,我們最終得到了 type f :: Typeable a => a -> IO ();它期望使用的型別為零(它想要處理“要么None或任何其他”)。
作為一個完全不同的軌道,你當然可以這樣做:
data None = None
data Option a = Some a | None' None
Now you've "created a new type to represent nothing" and "create a new union type for all a (normal value) and none". But Haskell's ADTs are discrimated unions, meaning there will always be a constructor tag (both at runtime and in your source code) on each variant in the union.
So there's really no point in having the separate None type, since once you've seen the None' tag in an Option a value you already know everything there is to know about the value; looking inside the None value to see that it is None tells you nothing2. So you might as well just use:
data Option a = Some a | None
Which is exactly the same as the built in Maybe, differing only in the names chosen.
Similarly you can obviously write a function with a case to handle when an Option is None, and a case to handle when there is something there (either of which can "do nothing", if you're returning a type where "doing something" makes any sense, i.e. some sort of action, like IO or State).
f :: Show a => Option a -> IO a
f None = pure ()
f (Some a) = print a
The code is slightly different, even ignoring for trivial syntax and naming issues; we had to reference the Some constructor and call print on the value inside it, rather than printing the argument to f directly3. And Haskell uses a typeclass divide types into ones that can meaningfully be printed and ones that can't, and only allow you to call print on the former. But it's so close that I have no reservations saying "this is the equivalent Haskell to the Typescript you wrote".
And given that Maybe already exists you might as well use it. (Similarly, should you ever need it the built in type () is - apart from the name - identical to data None = None.) But again: that is what you do if you are trying to solve a problem that you would use Option for in Typescript; if instead your goal is to implement something exactly the same as Option, then you simply can't with the tools Haskell gives you.
1 You can probably hack something truly horrible together using unsafe features (like unsafeCoerceing to and from Any). I do not know exactly how to go about making that usable and reliable, and doing so is an utter waste of time for any practical purpose; you would never use such code in a real program, it would just be an interesting exercise in how far you can hack the language implementation. So I'm not going to write an answer that addresses that angle.
2 Well, technically it sells you that the computation that produced it terminated; it could have been bottom. But you can't do anything with that information since it's impossible to test whether it was bottom; if it is you'll just error out (or not terminate) as well.
3 Printing the whole argument to f would have also worked, it would have just printed e.g. Some "value", which I assume is not what you meant.
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/434066.html
標籤:哈斯克尔
上一篇:如何撰寫自由單子
