在 function 中定義 Variable

在 Haskell 中的 variable 因為是 Immutable,所以算是 definition,負責程式碼中重複的部分;且有別於一般程式語言的是,由於 Haskell 將 function 也視為 data,因此 variable 也能是 function。

若使用 IIFE,則有另外一種使用 Lambda 方式。

Version


GHC 8.4.3

Haskell


Function

1
2
3
4
calChange given owed = 
if given - owed > 0
then given - owed
else 0

以上簡單的 function,我們發現幾個問題:

  • given - owed 不容易閱讀,應該取一個有意義的名稱代表其意義
  • given - owed 在 function 中重複運算

在 Haskell 中有三種解法:

  • where
  • let
  • lambda

Where

1
2
3
4
5
calChange given owed = 
if change > 0
then change
else 0
where change = given - owed

使用 where 定義 change,如此 ifthen 之後都可使用 change variable。

Let

1
2
3
4
5
calChange given owed = 
let change = given - owed
in if change > 0
then change
else 0

where 是定義在最後面,但也可使用 let 將 variable 定義在一開始,然後加上 in

Lambda

1
2
3
4
calChange given owed = 
(\change -> if change > 0
then change
else 0) (given - owed)

將非共用的部分抽成 Lambda,再將 given - owed 共用的部分以 parameter 傳入 Lambda。

其實這就是 ECMAScript 的 IIFE。

FSharp


Function

1
2
3
4
let calChange given owed =
if given - owed > 0
then given - owed
else 0

使用 let 定義 function,F# 與 Haskell 風格相當接近。

Let

1
2
3
4
5
let calChange given owed =
let change = given - owed
if change > 0
then change
else 0

使用 let 定義 change variable。

Lambda

1
2
3
4
let calChange given owed =
(fun change -> if change > 0
then change
else 0) (given - owed)

F# 的 Lambda 使用 fun,因為 F# 也支援 IIFE,因此寫法與 Haskell 很類似。

ECMAScript


Function

1
2
3
4
5
6
const calChange = (given, owed) => {
if (given - owed > 0)
return given - owed
else
return 0
};

最基本的寫法,使用 Arrow Function 定義 calChange()

1
2
const calChange = (given, owed) =>
given - owed > 0 ? given - owed : 0;

由於 ECMAScript 支援 ?: 寫法,可以稍微化簡。

Const

1
2
3
4
const calChange = (given, owed) => {
const change = given - owed;
return change > 0 ? change : 0;
};

類似 Haskell 的 let 寫法,先定義 change variable。

Arrow Function

1
2
const calChange = (given, owed) =>
(change => change > 0 ? change : 0)(given - owed);

如同 Haskell 的 Lambda 一樣,將非共用部分以 Arrow Function 表示,將共用的 given - owed 以 parameter 傳入。

這就是 IIFE,因為 ECMAScript 與 Haskell 都有 IIFE,所以能使用 Lambda 或 Arrow Function 抽出非共用部分。

CSharp


Function

1
2
3
4
5
6
7
Func<int, int, int> CalChange = (given, owed) =>
{
if (given - owed > 0)
return given - owed;
else
return 0;
};

使用 Func 定義 function,其餘寫法一樣。

1
2
Func<int, int, int> CalChange = (given, owed) => 
given - owed > 0 ? given - owed : 0;

由於 C# 支援 ?: 寫法,可以稍微化簡。

Var

1
2
3
4
5
Func<int, int, int> CalChange = (given, owed) =>
{
var change = given - owed;
return change > 0 ? change : 0;
};

類似 Haskell 的 let 寫法,先定義 change variable。

1
2
3
4
5
Func<int, int, int> CalChange = (given, owed) =>
{
Func<int, int> Body = change => change > 0 ? change : 0;
return Body(given - owed);
};

因為 C# 沒有 IIFE,因此只能先定義好 Body function,然後將 given - owed 傳入。

Conclusion


  • 傳統都會將共用部分抽成 Lambda 或 Arrow Function,但配合 IIFE,也看到另外一種用法,將非共用部分以 Lambda 表示,將共用部分以 parameter 傳入
  • C# 因為沒有 IIFE,所以使用 Lambda 的效果較不顯著

Reference


Will Kurt, Get Programming with Haskell

2018-10-21