學習軌道
/
Gleam
Gleam
/
練習
/
貼紙商店
貼紙商店

貼紙商店

學習練習

簡介

幻影型別

幻影型別是自訂型別的一種型別參數,它不會出現在該型別的任何值建構子裡。

這有點抽象,我們來看個例子吧:

pub type Length(unit) {
  Length(amount: Float)
}

在這個例子中,unit型別參數並沒有用在 Length值建構子裡,所以 unit就是幻影型別。

這個沒被用到的型別參數看起來好像沒什麼用,不過它可以替 Length值加上更多使用上的限制。

舉例來說,我們可以寫一個函式 double,把長度乘以二。它能處理任何單位的長度,所以它的型別參數是一個泛型型別變數。

// This function accepts all Length values
pub fn double(length: Length(unit)) -> Length(unit) {
  Length(length.amount *. 2.0)
}

我們也可以寫一個函式 add_inch,它只在長度單位是英吋時才能運作。

// A unit type for inches. It is never constructed so we don't
// define any constructors for it.
pub type Inches

pub fn add_inch(length: Length(Inches)) -> Length(Inches) {
  Length(length.amount +. 1.0)
}

add_inch函式不會接受其他單位參數的長度,這裡就是用幻影型別來確保只會使用正確的單位。

我們也可以寫一個函式,讓兩個長度值共用同一個型別變數,藉此確保它們的單位相同。

pub fn add(a: Length(unit), b: Length(unit)) -> Length(unit) {
  Length(a.amount +. b.amount)
}
let two_meters: Length(Meters) = Length(2.0)
let two_inches: Length(Inches) = Length(2.0)

add(two_meters, two_meters)
// -> Length(4.0): Length(Meters)

add(two_meters, two_inches)
// Type error! The unit type parameters do not match.

幻影型別和不透明型別搭配起來很好用。如果其他模組無法建構 Length值,我們就能確保它們不會用錯誤的單位型別來建構,也能確保只有上面定義的函式能搭配這些值使用。

說明

Lucy 經營著一家線上貼紙商店,專門販售以大家最愛的程式語言為主題的可愛貼紙。世界各地的顧客都會來向她購買貼紙,而面對這麼多種不同的貨幣,她應付起來有點吃力。

請寫一個程式,讓 Lucy 能用它來計算價格,同時確保她每次都使用正確的貨幣。

1. 定義Usd、Eur和Jpy型別

這些型別用來代表 Lucy 的顧客購買貼紙時所使用的各種貨幣。

它們要當作幽靈型別使用,不需要有任何建構子。

2. 定義Money型別

Money型別應該有一個代表金額的Int欄位、一個貨幣幽靈型別參數,並且是一個不透明型別。

3. 定義dollar、euro和yen函式

定義dollar、euro和yen函式,它們接受一個Int引數,並回傳帶有正確貨幣的Money值。

4. 定義total函式

定義total函式,它接受一個由Money值組成的陣列,並回傳該陣列中所有金額的總和。

total([euro(120), euro(200), euro(145)])
// -> euro(465)
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Gleam Exercism

準備好開始 貼紙商店 了嗎?

註冊 Exercism,透過 33 個概念122 個練習 和真人引導來學習並精通 Gleam,全部免費。