openした結果モジュールが被ってしまった場合でも両方のモジュールの関数を扱える
この記事はF# Advent Calendar 2014 - connpassの23日目の記事です。
(日にち変更していただきました。ありがとうございます。)
昨日はむろほしりょうたさんの初心者がF#をUnityで使ってみた! - Qiitaでした。
F# のネタリスト - 2つのアンコールより、
「openした結果モジュールが被ってしまった場合でも両方のモジュールの関数を扱えること知りたいです」について書きます。
openした結果
MSDN
F# コンパイラでは、 複数の開いているモジュールまたは名前空間に同じ名前が存在し、 あいまいさが生じる場合でも、エラーや警告を生成しません。 あいまいさが生じた場合、F# では、 開いた時間が新しい方のモジュールまたは名前空間が優先されます。
これですね。
確認してみる
今回は「モジュールが被ってしまった場合」なので、同じ名前のmoduleを用意します。
関数名も同じにしておきました。
(* Tax.fs *) namespace Advent.Tax module Calc = let taxRate = 0.08 let getPrice basePrice = basePrice * (1.0 + taxRate)
税率を指定して、getPriceに本体価格を渡すと税込価格を返してくれる。
(* Sale.fs *) namespace Advent.Sale module Calc = let discountRate = 0.2 let getPrice basePrice = basePrice * (1.0 - discountRate)
値引率を指定して、getPriceに本体価格を渡すと値引き後の価格を返してくれる。
openします。
(* Display.fs *) namespace Advent open Advent.Tax open Advent.Sale module Display = let basePrice = 100.0 let paymentPrice = Calc.getPrice basePrice printf "お支払金額 %A 円" paymentPrice
実行。
お支払金額 80.0 円
2割引のセール価格が表示されました。
open2行の上下をを入れ替えて実行。
(* Display.fs *) namespace Advent open Advent.Sale open Advent.Tax module Display = let basePrice = 100.0 let paymentPrice = Calc.getPrice basePrice printf "お支払金額 %A 円" paymentPrice
お支払金額 108.0 円
税込価格が表示されました。
どちらも後で開いた方が優先されています。
両方のモジュールの関数を扱える
両方のモジュールの関数を使って、
セール価格にした後、消費税をのせた額をお支払金額をとして表示しましょう。
openのタイミングを変える
(* Display.fs *) namespace Advent open Advent.Sale module Display = let basePrice = 100.0 let salePrice = Calc.getPrice basePrice open Advent.Tax (* ここでopen *) let paymentPrice = Calc.getPrice salePrice printf "お支払金額 %A 円 (税抜価格 %A 円)" paymentPrice salePrice
お支払金額 86.4 円 (税抜価格 80.0 円)
セール価格にして、消費税をのせて、お支払金額にすることはできましたが、
両方の関数を扱えるという感じではありません。
Taxのopen前はSaleしか使えず、open後はTaxしか使えません。
フル修飾する
(* Display.fs *) namespace Advent module Display = let basePrice = 100.0 let salePrice = Advent.Sale.Calc.getPrice basePrice let paymentPrice = Advent.Tax.Calc.getPrice salePrice printf "お支払金額 %A 円 (税抜価格 %A 円)" paymentPrice salePrice
お支払金額 86.4 円 (税抜価格 80.0 円)
できました。
1回しか書かないならこれでもいいかもしれません。
別名をつける
(* Display.fs *) namespace Advent module Display = module Sale = Advent.Sale.Calc module Tax = Advent.Tax.Calc let basePrice = 100.0 let salePrice = Sale.getPrice basePrice let paymentPrice = Tax.getPrice salePrice printf "お支払金額 %A 円 (税抜価格 %A 円)" paymentPrice salePrice
お支払金額 86.4 円 (税抜価格 80.0 円)
何度も使うならこれが便利そうですね。
ちなみに
名前が被っていない定義や関数は、特に何もしなくても両方扱えます。
Tax.fsとSale.fsに定義と関数を追加して確認します。
(* Tax.fs *) namespace Advent.Tax module Calc = let taxRate = 0.08 let getPrice basePrice = basePrice * (1.0 + taxRate) (* 以下を追加 *) let oldTaxRate = 0.05 let getOldPrice basePrice = basePrice * (1.0 + oldTaxRate)
(* Sale.fs *) namespace Advent.Sale module Calc = let discountRate = 0.2 let getPrice basePrice = basePrice * (1.0 - discountRate) (* 以下を追加 *) let timesaleDiscountRate = 0.3 let getTimeSalePrice basePrice = basePrice * (1.0 - timesaleDiscountRate)
(* Display.fs *) namespace Advent open System open Advent.Sale open Advent.Tax (* Taxをあとにしてまとめてopen *) module Display = let basePrice = 100.0 let toPercentage rate = rate * 100.0 let paymentPrice = Calc.getPrice basePrice (* TaxのgetPrice *) let taxPer = Calc.taxRate |> toPercentage let oldPaymentPrice = Calc.getOldPrice basePrice let oldTaxPer = Calc.oldTaxRate |> toPercentage let timesalePrice = Calc.getTimeSalePrice basePrice let timesaleDiscountPer = Calc.timesaleDiscountRate |> toPercentage printf "現在のお支払金額 %A 円 (消費税 %A %)\n" paymentPrice taxPer printf "以前のお支払金額 %A 円 (消費税 %A %)\n" oldPaymentPrice oldTaxPer printf "タイムセール価格 %A 円 (%A %引)" timesalePrice timesaleDiscountPer
現在のお支払金額 108.0 円 (消費税 8.0 %) 以前のお支払金額 105.0 円 (消費税 5.0 %) タイムセール価格 70.0 円 (30.0 %引)
ここまでのまとめ
既存モジュールを(擬似的に)拡張したり書き換えたりする
まとめを踏まえて。
既存モジュールと同名のモジュールを作ることで、
既存モジュールを拡張したり書き換えたりしたように見せられる!
ということを書いてみます。
(* StrEx.fs *) namespace Advent.StrEx (* Core.Stringの拡張,書き換え用Stringモジュールを作る *) module String = let greeting (str:string) = "Happy Holidays!" let length (str:string) = str.Length.ToString() + "文字"
(* Program.fs *) namespace Advent open Advent.StrEx (* Core.Stringは暗黙で開かれるのでこれが後になる *) module Execute = let baseString = "aaa" (* 自作関数greetingをString.greetingで使える *) let greetingString = String.greeting baseString printf "%A\n" greetingString (* Core.Stringの関数も使える *) let strings = [baseString; "bbb"] let concatString = String.concat "," strings printf "%A\n" concatString (* Core.StringのlengthではなくStrExのlengthが使える *) let length = String.length baseString printf "%A" length
"Happy Holidays!" "aaa,bbb" "3文字"