Skip to content

Instantly share code, notes, and snippets.

@Gab-km
Last active August 29, 2015 14:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Gab-km/11242018 to your computer and use it in GitHub Desktop.
Save Gab-km/11242018 to your computer and use it in GitHub Desktop.
F# Tips inspired by @kmizu's Scala Tips for newbies

これは、 @kmizu さんの「初学者向けの Scala Tips」を勝手に F# にポートした記事です。

元記事

初学者向けの Scala Tips (1) - Option#map()を使おう

F# の方でも Option.map があります。ただ Option.mapOption モジュールの持ち物であり、また map 関数のシグネチャが ('T -> 'U) -> 'T option -> 'U option であるという違いがあります。

Option.map を使わない時:

let result =
  match exp1 with
  | Some(v1) ->
      let vx = //expression using v1
      Some(vx)
  | None -> None

Option.map を使う時:

let result =
  Option.map (fun v1 ->
    let vx = //expression using v1
    vx
  ) exp1

こんな感じですね。

より一般的なコードでは、flatMap()を使う事が有用な事がありますが、

Scala の flatMap メソッドに対応するのは、 F# だと bind 関数だと思います。 Option 以外の collections 名前空間にいるようなコンテナ型(ListSeq など)だと collect 関数というのがそれに当たります。

元記事

初学者向けの Scala Tips (2) - nullからOptionへの変換

null 死すべし。慈悲はない。これは F# でも尊ばれている思想です。平安時代の何とかっていうサムライも言ってた。

let fsMap: Map<string, string> = Map.ofList listOfTupleValues
let value: string = Map.find key fsMap
// 実は、以下のコードはあまり意味がない
if value <> null then
    ...
else
    ...

さて、このサンプルコードですが、kmizu さんの Scala 版とは挙動が違います。何が違うかというと、 Map.find は対応する keyfsMap にない場合、null を返したりせず KeyNotFoundException を送出します。ダメじゃん。

ということで、 Map.findMap.tryFind で書き換えたものがこちら。Map.tryFind は結果を Option<'T> に包んで返してくれるので、元記事の2つ目のサンプルコードとほとんど同じコードになります。

let fsMap: Map<string, string> = Map.ofList listOfTupleValues
let value: string option = Map.tryFind key fsMap
match value with
| Some(v) -> ...
| None    -> ...

なお、今回のサンプルには出てきませんでしたが、null から Option<'T> 型の値にする標準関数は提供されていません(何だと…)。ご入用でしたら、次のような関数を定義しておくと便利かもしれません。

let toOption (value: 'T when 'T : null) =
  if value = null then
    None
  else
    Some(value)                                  
元記事

初学者向けの Scala Tips (3) - 型に対するマッチより構造に対するマッチ

@kmizu さんの記事で、

この機能、しばしば型に対するswitch-caseとして使われていることがあるようです。

とあったんですが、折角のパターンマッチをそんな風に使うなんてもったいない!

ところで、 Scala の方で case class を使っていますが、 F# では判別共用体を使います。

type Value =
    | Hoge of int
    | Foo of string
    | Bar of string list

さて、この Value をわざわざ型でパターンマッチする、というサンプルを用意したかったんですが、残念ながら F# の判別共用体ではそれが出来ません。それは、 Scala で case class を用いている箇所は、 F# においては「ラベルとコンストラクタ」(これを、ケース識別子、とかいう)としてしかアクセス出来ません。そして、このケース識別子をコンストラクタとして用いて得た値は、 Value 型の値となります。つまり、型チェックのしようがないのです…。

ですので、パターンマッチのサンプルコードとしては後者の方が F# で書きなおすことが出来ます。

match value with
| Hoge(v) -> doHoge v
| Foo(v) -> doFoo v
| Bar(v) -> doBar v

ちなみに、 F# で型に対するパターンマッチをするには、次のようにします:

match value with
| :? Derived1 -> printfn "Derived1 class"
| :? Derived2 -> printfn "Derived2 class"
| _           -> printfn "Base class"

ということで、 F# の判別共用体では、普通に識別子パターンによるマッチングをしましょう。

元記事

初学者向けの Scala Tips (4) - パターンマッチのcase節には複数の式が書ける

はい、今回は書くことが無いですね…。 F# のパターンマッチは、中括弧(brace)で囲んだりする必要がありません。

強いて言えば、同じ処理をさせたいケースをまとめることができる、くらいでしょうか。

// 1つずつ書く
match value with
| 2 -> printfn "東工大生「うおおぉぉぉぉ!!!」"
| 3 -> printfn "東工大生「うおおぉぉぉぉ!!!」"
| 5 -> printfn "東工大生「うおおぉぉぉぉ!!!」"
| 7 -> printfn "東工大生「うおおぉぉぉぉ!!!」"
| _ -> printfn "東工大生「…」"

// まとめて書く
match value with
| 2 | 3 | 5 | 7 -> printfn "東工大生「うおおぉぉぉぉ!!!」"
| _             -> printfn "東工大生「…」"
元記事

初学者向けの Scala Tips (5) - パターンマッチと無名関数の組み合わせを簡潔に書く

F# ですと、次のようなコード:

List.map (fun x ->
  match x with
  | A -> ...
  | B -> ...
  | _ -> ...
  )

に対して、このような書き換えができるかなぁという感じです:

List.map (function
  | A -> ...
  | B -> ...
  | _ -> ...
  )

なお、 PartialFunction は Scala 特有というか、F# にはない概念ですね。しいて言えば、パーシャル アクティブ・パターンが近いかな?

元記事

初学者向けの Scala Tips (6) - 文字列リテラル中のインデントを整える

えーと、今回は F# 側で対応する機能がありません…。

しいて言えば、F# では三重引用符を使わなくても文字列中に改行を含めることが出来ます。

> let hoge = """
- type Foo() =
-     member self.foo() = println "foo"
- """;;

val hoge : string = "
type Foo() =
    member self.foo() = println "foo"
"

> let fuga = "
- type Foo() =
-     member self.foo() = println \"foo\"
- ";;

val fuga : string = "
type Foo() =
    member self.foo() = println "foo"
"
元記事

初学者向けの Scala Tips (7) - objectで名前空間を作る

名前空間を分けておくというの、F# においても大事な設計判断です。

(* 注: 判別共用体にフィールド名を使えるのは F# 3.1 以降です *)
type Expression =
  | Addition of lhs: Expression * rhs: Expression
  | Subtraction of lhs: Expression * rhs: Expression
  | Multiplication of lhs: Expression * rhs: Expression
  | Division of lhs: Expression * rhs: Expression
  | Number of int

ここで、まず普通に名前空間に型を配置することが出来ます。

namespace AST

  type Expression =
    | Addition of lhs: Expression * rhs: Expression
    | Subtraction of lhs: Expression * rhs: Expression
    | Multiplication of lhs: Expression * rhs: Expression
    | Division of lhs: Expression * rhs: Expression
    | Number of int

一方、F# にはモジュールというものがあり、モジュールに型を持たせることも出来ます。

module AST =

  type Expression =
    | Addition of lhs: Expression * rhs: Expression
    | Subtraction of lhs: Expression * rhs: Expression
    | Multiplication of lhs: Expression * rhs: Expression
    | Division of lhs: Expression * rhs: Expression
    | Number of int

どちらの場合でも、 AST.Expression とか AST.Subtraction とやって使うことが出来ます。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment