2013-08-24 43 views
4

我很想有let構造與Scala中的Haskell類似。我嘗試了幾種方法,但沒有一個好。下面是一些代碼:在Scala中定製「讓」表達式

object CustomLet extends App { 
    val data = for (i <- 1 to 1024; j <- 1 to 512) yield (i % j) * i * (i + 1) - 1 

    def heavyCalc() = { println("heavyCalc called"); data.sum } 

    def doSomethingWithRes(res: Int) = { 
    println(s"${res * res}") 
    1 
    } 

    def cond(value: Int): Boolean = value > 256 

    // not really usable, even though it's an expression (2x heavyCalc calls) 
    def withoutLet() = if (cond(heavyCalc())) doSomethingWithRes(heavyCalc()) else 0 

    // not an expression 
    def letWithVal(): Int = { 
    val res = heavyCalc() 
    if (cond(res)) doSomethingWithRes(res) 
    else 0 
    } 

    // a lot of code to simulate "let", at least it is an expression 
    def letWithMatch(): Int = heavyCalc() match { 
    case res => if (cond(res)) doSomethingWithRes(res) else 0 
    } 

    // not perfect solution from 
    // http://stackoverflow.com/questions/3241101/with-statement-equivalent-for-scala/3241249#3241249 
    def let[A, B](param: A)(body: A => B): B = body(param) 

    // not bad, but I'm not sure if it could handle more bindings at once 
    def letWithApp(): Int = let(heavyCalc()) {res => if (cond(res)) doSomethingWithRes(res) else 0} 

    List[(String,() => Int)](
    ("withoutLet", withoutLet), 
    ("letWithVal", letWithVal), 
    ("letWithMatch", letWithMatch), 
    ("letWithApp", letWithApp) 
).foreach(
    item => item match { 
     case (title, func) => { 
     println(s"executing $title") 
     val ret = func() 
     println(s"$title finished with $ret") 
     println() 
     } 
    } 
) 
} 

這是它的理想的樣子(只有一個綁定,更可以通過,分離;不能確定in關鍵字):

// desired look 
    def letTest(): Int = 
    let res = heavyCalc() in 
     if (cond(res)) doSomethingWithRes(res) else 0 

我不是當然,如果可能的話,但我沒有像大多數先進的Scala東西的經驗,所以我真的不知道。

EDIT1:爲了清楚起見,我期待它的主要的事情是:爲表達和相對簡單的語法(如上面概述)。

+0

對於我們這些不講哈斯克爾的人,你能解釋一下「讓」應該做什麼嗎? –

+0

'letWithVal'與'letTest'中的'let'完全相同。 – monnef

回答

6

你可以使用一個去路管

object ForwardPipeContainer { 
    implicit class ForwardPipe[A](val value: A) extends AnyVal { 
    def |>[B](f: A => B): B = f(value) 
    } 
} 

使用這樣的:

import ForwardPipeContainer._ 

def f(i: Int) = i * i 

println(f(3) |> (x => x * x)) 

你可以把多個參數中的一個元組:

println((f(2), f(3)) |> (x => x._1 * x._2)) 

如果與部分功能synatx組合看起來更好:

println((f(2), f(3)) |> { case (x, y) => x * y }) 

這個答案是What is a good way of reusing function result in Scala的變化,並且兩者都基於Cache an intermediate variable in an one-liner那裏我得到了從最初的想法。

3
def letTest(): Int = 
    let res = heavyCalc() in 
     if (cond(res)) doSomethingWithRes(res) else 0 

我會寫這樣的:

def letTest(): Int = { 
    val res = heavyCalc() 
    if (cond(res)) doSomethingWithRes(res) else 0 
} 

忽略懶惰,僅僅是介紹了一個詞法範圍的構建,結合一些條款,一些名字,然後返回表達式。所以,在Scala中,你會怎麼做,如果你想確保沒有別的塊中的事情

{ // new lexical scope 
    // bind terms section 
    val a = f() 
    def b = a + g() // may be I don't want g to be evaluated unless b is needed 
    val c = h() 
    // result expression 
    if (c) b else a 
} 

宏應能強制執行此語法佈局。實際上有一個名爲Spores的SIP(Scala改進流程)提案,它會強制執行一些相同的約束(以及另外一個約束:您不會在不知情的情況下捕獲封閉對象的引用)。

請注意,Scala中的塊是表達式,用於計算塊中的最後一個表達式。所以讓我來random讓從哈斯克爾例如:

aaa = let y = 1+2 
      z = 4+6 
      in let f = 3 
       e = 3 
      in e+f 

這相當於:

val aaa = { 
    val y = 1 + 2 
    val z = 4 + 6 
    val u = { 
    val f = 3 
    val e = 3 
    e + f 
    } 
    u 
} 

正如你所看到的語句塊可以作爲一種表達。

+0

對於'val'解決方案 - 它不是一個表達式(實際上與來自問題的'letWithVal'相同)。 – monnef

+1

@monnef,啊,你已經考慮過了。那麼它實際上*是一個表達式(請參閱我的評論)。 – huynhjl

+0

是的,你是對的。這是一個表達式,但不是我正在尋找的東西 - 我想避免命令'val/var'和塊。如果我的問題沒有完全說明,我很抱歉。 (我明白,在引擎蓋下它會翻譯成類似於val/var的東西,我正在尋找一種方式來以一種功能性的方式進行簡單的綁定。) – monnef