Roc言語入門:関数型プログラミングの新しいアプローチ


プログラミング言語の世界では、常に新しい言語が登場していますが、その中でも特に注目を集めているのが Roc(ロック) です。Rocは、関数型プログラミングの利点を活かしながら、高速で使いやすいことを目指して開発されている新しい言語です。

本記事では、Rocの特徴、基本的な文法、他の言語との違い、そして実際の使い方について詳しく解説します。

Rocとは

Rocは、Richard Feldman氏が中心となって開発している関数型プログラミング言語です。Elmの影響を強く受けながら、より汎用的で高速な言語を目指しています。

主な特徴

  • 関数型プログラミング: 純粋関数と不変性を基本とした設計
  • 高速: LLVMバックエンドによる最適化されたネイティブコード生成
  • 型安全: 強力な型システムによるコンパイル時のエラー検出
  • シンプルな文法: 学習しやすく、読みやすいコード
  • ゼロコスト抽象化: 高レベルの抽象化でもパフォーマンスを犠牲にしない
  • メモリ安全: Rustのような所有権システムなしでメモリ安全を実現

Rocのインストール

macOS/Linux

# 最新版をダウンロード
curl -OL https://github.com/roc-lang/roc/releases/latest/download/roc_nightly-macos_apple_silicon.tar.gz

# 解凍
tar -xzf roc_nightly-macos_apple_silicon.tar.gz

# パスを通す
export PATH=$PATH:$(pwd)/roc_nightly-macos_apple_silicon

# 確認
roc version

Windows

# PowerShellで実行
Invoke-WebRequest -Uri https://github.com/roc-lang/roc/releases/latest/download/roc_nightly-windows.zip -OutFile roc.zip
Expand-Archive roc.zip -DestinationPath .
$env:PATH += ";$(pwd)\roc_nightly-windows"
roc version

Hello, World!

まずは、Rocの基本的なプログラムを見てみましょう。

# hello.roc
app "hello"
    packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.7.0/bkGby8jb0tmZYsy2hg1E_B2QrCgcSTxdUlHtETwm5m4.tar.br" }
    imports [pf.Stdout]
    provides [main] to pf

main =
    Stdout.line "Hello, World!"

実行:

roc run hello.roc
# 出力: Hello, World!

プログラムの構造

  • app: アプリケーション名を定義
  • packages: 使用するパッケージを指定
  • imports: インポートするモジュールを指定
  • provides: このモジュールが提供する関数を指定
  • main: エントリーポイント

基本的な文法

変数と束縛

Rocでは、すべての値が不変です。

# 変数の束縛
x = 42
name = "Alice"
isActive = Bool.true

# 複数の値を束縛
point = { x: 10, y: 20 }

関数

関数は純粋で、副作用を持ちません。

# 関数の定義
add : I64, I64 -> I64
add = \x, y -> x + y

# 使用例
result = add 10 20  # 30

# 複数行の関数
factorial : I64 -> I64
factorial = \n ->
    if n <= 1 then
        1
    else
        n * factorial (n - 1)

型システム

Rocは強力な型推論を持ちますが、型注釈も書けます。

# 数値型
age : I64
age = 30

temperature : F64
temperature = 36.5

# 文字列
name : Str
name = "Bob"

# ブール値
isValid : Bool
isValid = Bool.true

# リスト
numbers : List I64
numbers = [1, 2, 3, 4, 5]

# レコード(構造体)
person : { name : Str, age : I64 }
person = { name: "Charlie", age: 25 }

パターンマッチング

# 数値のパターンマッチング
describe : I64 -> Str
describe = \n ->
    when n is
        0 -> "zero"
        1 -> "one"
        2 -> "two"
        _ -> "many"

# リストのパターンマッチング
sumList : List I64 -> I64
sumList = \list ->
    when list is
        [] -> 0
        [first, .. as rest] -> first + sumList rest

# レコードのパターンマッチング
greet : { name : Str, age : I64 } -> Str
greet = \person ->
    when person is
        { name: "Alice", age } -> "Hello Alice, you are \(Num.toStr age)"
        { name, age } -> "Hello \(name)"

Result型(エラーハンドリング)

Rocでは、エラーを Result 型で表現します。

# Result型の定義
# Result ok err = Ok ok | Err err

divide : F64, F64 -> Result F64 [DivisionByZero]
divide = \a, b ->
    if b == 0.0 then
        Err DivisionByZero
    else
        Ok (a / b)

# Resultの使用
handleDivision : F64, F64 -> Str
handleDivision = \a, b ->
    when divide a b is
        Ok result -> "Result: \(Num.toStr result)"
        Err DivisionByZero -> "Error: Cannot divide by zero"

リスト操作

# リストの作成
numbers = [1, 2, 3, 4, 5]

# map
doubled = List.map numbers \n -> n * 2
# [2, 4, 6, 8, 10]

# filter
evens = List.filter numbers \n -> n % 2 == 0
# [2, 4]

# reduce (fold)
sum = List.walk numbers 0 \acc, n -> acc + n
# 15

# リストの結合
combined = List.concat [1, 2] [3, 4]
# [1, 2, 3, 4]

実践例

1. FizzBuzz

app "fizzbuzz"
    packages { pf: "..." }
    imports [pf.Stdout]
    provides [main] to pf

fizzBuzz : I64 -> Str
fizzBuzz = \n ->
    when (n % 3, n % 5) is
        (0, 0) -> "FizzBuzz"
        (0, _) -> "Fizz"
        (_, 0) -> "Buzz"
        _ -> Num.toStr n

main =
    List.range { start: At 1, end: At 100 }
    |> List.map fizzBuzz
    |> Str.joinWith "\n"
    |> Stdout.line

2. ファイル読み込み

app "fileReader"
    packages { pf: "..." }
    imports [pf.Stdout, pf.File, pf.Path]
    provides [main] to pf

main =
    path = Path.fromStr "data.txt"

    when File.readUtf8 path is
        Ok content ->
            Stdout.line "File content: \(content)"

        Err _ ->
            Stdout.line "Error: Could not read file"

3. JSONパース

app "jsonParser"
    packages { pf: "...", json: "..." }
    imports [pf.Stdout, json.Core]
    provides [main] to pf

User : { name : Str, age : I64, email : Str }

parseUser : Str -> Result User [ParseError]
parseUser = \jsonStr ->
    Core.parseStr jsonStr
    |> Result.try \json ->
        name = Core.field "name" Core.string json
        age = Core.field "age" Core.i64 json
        email = Core.field "email" Core.string json

        when (name, age, email) is
            (Ok n, Ok a, Ok e) -> Ok { name: n, age: a, email: e }
            _ -> Err ParseError

main =
    jsonData = """{"name": "Alice", "age": 30, "email": "alice@example.com"}"""

    when parseUser jsonData is
        Ok user ->
            Stdout.line "User: \(user.name), Age: \(Num.toStr user.age)"

        Err _ ->
            Stdout.line "Error: Invalid JSON"

4. HTTPリクエスト

app "httpClient"
    packages { pf: "...", http: "..." }
    imports [pf.Stdout, http.Http]
    provides [main] to pf

fetchUser : I64 -> Task User [HttpError]
fetchUser = \userId ->
    url = "https://api.example.com/users/\(Num.toStr userId)"

    Http.get url
    |> Task.await \response ->
        when response.status is
            200 -> parseUser response.body
            _ -> Task.err HttpError

main =
    Task.attempt (fetchUser 1) \result ->
        when result is
            Ok user ->
                Stdout.line "User: \(user.name)"

            Err _ ->
                Stdout.line "Error: Could not fetch user"

Rocの独自機能

1. Abilities(能力)

Abilitiesは、Rocの型クラスに相当する機能です。

# Abilityの定義
Hash has
    hash : a -> U64 where a has Hash

# 実装
hash : I64 -> U64 where I64 has Hash
hash = \n ->
    Num.toU64 (Num.abs n)

hash : Str -> U64 where Str has Hash
hash = \s ->
    # ハッシュ関数の実装
    ...

2. プラットフォーム

Rocでは、「プラットフォーム」という概念を使用して、異なる環境での実行をサポートします。

# CLIプラットフォームを使用
app "myApp"
    packages { pf: "basic-cli" }
    imports [pf.Stdout, pf.Stdin]
    provides [main] to pf

# Webプラットフォームを使用
app "myWebApp"
    packages { pf: "basic-webserver" }
    imports [pf.Http]
    provides [main] to pf

3. バックパッシング

Rocの独特な構文として、バックパッシング(<-)があります。

# バックパッシングなし
readAndPrint : Task {} [FileError, IoError]
readAndPrint =
    Task.await (File.readUtf8 "input.txt") \content ->
        Task.await (processContent content) \result ->
            Stdout.line result

# バックパッシングあり
readAndPrint : Task {} [FileError, IoError]
readAndPrint =
    content <- Task.await (File.readUtf8 "input.txt")
    result <- Task.await (processContent content)
    Stdout.line result

RocとRustの比較

特徴RocRust
パラダイム関数型マルチパラダイム
メモリ管理自動(GC不使用)所有権システム
学習曲線比較的緩やか急峻
パフォーマンス高速最高速
型システムシンプルで強力非常に強力
エコシステム発展途上成熟

コード比較

Rustの例:

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    let sum: i32 = numbers.iter().sum();
    println!("Sum: {}", sum);
}

Rocの例:

main =
    numbers = [1, 2, 3, 4, 5]
    sum = List.walk numbers 0 \acc, n -> acc + n
    Stdout.line "Sum: \(Num.toStr sum)"

Rocのエコシステム

パッケージマネージャー

Rocは組み込みのパッケージマネージャーを持っています。

app "myApp"
    packages {
        pf: "https://github.com/roc-lang/basic-cli/releases/...",
        json: "https://github.com/lukewilliamboswell/roc-json/releases/...",
    }
    imports [pf.Stdout, json.Core]
    provides [main] to pf

エディタサポート

  • VS Code: Roc Language Server拡張機能
  • Vim/Neovim: roc.vim プラグイン
  • Emacs: roc-mode

パフォーマンス

Rocは高速な実行速度を目指して設計されています。

ベンチマーク例

# フィボナッチ数列(再帰版)
fibonacci : I64 -> I64
fibonacci = \n ->
    if n <= 1 then
        n
    else
        fibonacci (n - 1) + fibonacci (n - 2)

# フィボナッチ数列(最適化版)
fibonacciOptimized : I64 -> I64
fibonacciOptimized = \n ->
    fibHelper n 0 1

fibHelper : I64, I64, I64 -> I64
fibHelper = \n, a, b ->
    if n == 0 then
        a
    else
        fibHelper (n - 1) b (a + b)

Rocの今後

Rocはまだ開発中の言語ですが、以下の機能が計画されています。

  • 並行処理: 軽量スレッドとアクターモデル
  • より豊富な標準ライブラリ: ネットワーク、データベース、GUIなど
  • パッケージリポジトリ: 公式パッケージレジストリ
  • 安定版リリース: 1.0リリースに向けた開発

まとめ

Rocは、関数型プログラミングの利点を活かしながら、高速で使いやすい言語を目指しています。まだ開発段階ですが、独自の設計思想と強力な型システムにより、今後の発展が期待されます。

Rocが適しているケース:

  • 関数型プログラミングを学びたい
  • 高速なCLIツールやバックエンドサービスを構築したい
  • シンプルで読みやすいコードを書きたい
  • 新しい技術に挑戦したい

現時点での課題:

  • エコシステムが未成熟
  • ドキュメントが限定的
  • 本番環境での使用にはまだ早い

Rocは今後の発展が非常に楽しみな言語です。興味がある方は、ぜひ試してみてください。

参考リンク