為了在函式背景關系中獲得順序評估,在 JavaScript 中,我經常使用
const right = a => b => b;
const f = () => right(console.log("hi"))(true);
console.log(f());
這有點類似于do執行緒,并且實作起來更簡單,但僅適用于 JavaScript 等急求評估語言。
在 Haskell 中,由于該語言以惰性求值/按需呼叫的方式作業,因此a不會對 的引數進行求值,因為 lambda 運算式主體中不需要它。
所以,我想知道right在惰性評估中實作該功能的簡單智能方法是什么。 <- 這是我的第一個問題。
有 Haskell wiki 文章:
評估的順序
根據
seqHaskell 2010 報告,盡管它的名字叫原語,但不需要以某種預定義的順序評估其引數。對于并行編程,有時需要 seq 的變體,并具有這樣的順序:
雖然我們主要關注實作,但我們的作業對編程模型產生了一些影響:我們確定了對 pseq 和 seq 的需求(第 2.1 節),并且我們在撰寫并行程式的“策略”方法中發現了一個重大困難(第 7 節)。多核 Haskell 的運行時支持;西蒙·馬洛、西蒙·佩頓·瓊斯和薩特南·辛格。代替具有相似目的的兩個或多個相似原語,使用傳統的引數評估順序擴展現有的 seq 原語 - 首先,然后是第二;由具有更合適名稱的新原語執行簡單嚴格控制的角色,例如
amid,在沒有排序或混淆的嚴格性中。
和
嚴格無序或混亂
As the Haskell 2010 Report does not specify any order of evaluation with respect to its parameters, the name of the primitive
seq :: a -> b -> bis a misnomer.
Introduce the primitive
amid, with the same (Haskell 2010 Report) requirements:
infixr 0 `amid`
primtive amid :: a -> b -> b
infixr 0 $!
($!) :: (a -> b) -> a -> b
f $! x = x `amid` f x
Actually, the function is exactly the same as my right; however, what I don't understand is as I mentioned in lazy evaluation, since a is not required in the function body, it shall not be evaluated. So what happens here? #2 Question
A code I think is
right = \a -> \b -> (const a . const b) 0
but, I don't know this one stably works.
uj5u.com熱心網友回復:
一個主要的區別是 Haskell 是純的,而 JavaScript 不是。在 Haskell 中,如果從不使用計算結果,則無論是否計算(忽略運行時)對程式的語意都沒有影響。這是因為您不能在純函式中產生副作用,其中包括將內容列印到控制臺,因此如果您不使用該函式的結果,就好像您從未呼叫它一樣。
列印輸出的唯一方法,即具有不純函式,是它是否在IOmonad 中。所以像這樣的函式:
f :: Int
f = const 5 (putStrLn "hello")
-- const is just your `right` function with its arguments reversed
不可能列印任何東西,因為該函式是純函式(如型別簽名所示)。
即使你使用seq
f :: Int
f = let x = putStrLn "hello" in x `seq` const 5 x
您將看不到任何列印的內容 - 因為seq只會強制x使用弱頭部正常形式,即僅評估IO建構式,但對IO動作的評估并不意味著它的執行。
(編輯:使效果真正發生的唯一方法IO是讓它在main,這是一個特殊的IO動作。在上面的定義中f,運算式的計算結果為5 :: Int并且putStrLn "hello"對于任何外部函式都不存在,所以它永遠不會成為其中的一員main。感謝@amalloy 的更正。)
這樣做的唯一方法是使用該>>=函式。但是, 的第二個引數>>=需要回傳一個IO a. 這意味著您只能強制發生副作用,然后回傳一個值,如果該值是在IOmonad 中回傳的。
總之,單子是保證您正在尋找的順序評估的唯一方法:
f :: IO Int
f = do putStrLn "hello"
return 5
或者等價地,
f = putStrLn "hello" >> return 5
-- Can also be written as:
f = putStrLn "hello" >>= \_ -> return 5
uj5u.com熱心網友回復:
Haskell 是懶惰和純粹的事實有兩個與您的問題相關的結果:
1.你不能寫這個函式
語言中根本沒有工具可以讓您說“評估這個,然后評估那個”。正如您發現seq的最接近的那樣,它確保在回傳右元素之前評估其左引數。但它可能會評估b, then ,然后如果喜歡a就回傳。b
2.你不需要這個功能
在 Haskell 中評估一個運算式永遠不會1有任何可觀察到的副作用。因此flip const,等價于(\x y -> y),具有與您想象的此功能相同的行為。它回傳它的第二個引數,您可以想象如果您愿意,它會評估左邊的引數,這沒有任何效果,因為評估事物永遠不會。您提出的實作right是錯誤的,因為它回傳左引數而不是右引數,但除此之外它再次具有相同的行為。
在您確實需要順序排序的情況下,它不是評估的排序,而是嵌入在 IO中的效果的排序。Vikstapolis 的回答已經很好地涵蓋了這個主題,所以我不再贅述。
1好的,實際上這條規則有很多例外。但他們都以某種方式作弊,并沒有真正考慮到這里。一些最常見的例外情況:
- 評估運算式可能會分配記憶體,或導致一些當前未完成的作業實際完成。如果您需要這些效果,這正是您想要
seq的。 - Debug.Trace 允許您將日志寫入控制臺,作為評估其他純運算式的一部分。這僅用于除錯,因此沒有用于微調排序的標準庫函式:如果您關心運算式產生的日志,那么您也應該真正關心運算式本身。Debug.Trace 中的東西已經建立在上面
seq,所以如果left只記錄一些東西,你可以只使用 Debug.Trace 中的一個函式,例如trace "hi" True. unsafePerformIO可以在所謂的純運算式中執行任意 IO。它是為非常低級的東西保留的工具,即使是語言專家也避開了,除非有迫切需要。初學者不應該擔心這個功能。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/427287.html
標籤:haskell
