LED通信事業プロジェクト エンジニアブログ

F#でラズパイを動かす(1)

ラズパイと.NET

記事更新日 2022年1月11日


はじめに

2022年最初のブログ更新となります。本年もどうぞよろしくお願いいたします。

年が変わったこともありまして、今回よりブログとしても新しいネタも扱いたいと思います。それは、ずばりMicrosoft F#

LEDバックホールのようないろいろな場所に置かれる通信機を使っている場合、リモート操作や監視など別途コンピューターを使わなければならない場面が結構あります。また、LEDバックホール自体は単なる通信装置ですが、これまでなかった新しい通信機ということで「何に使えばいいの」というご質問を多数頂きます。ですから、LEDバックホールを通信機単体ではなく「通信を含めたアプリケーション」としてご紹介していかないと、なかなかお客様の反応も芳しくない・・・ そんな理由から、例えば展示会なんかで、何か動くものとかアプリとかとくっつけて通信機を紹介するという機会が結構あります(そういえば、展示会でのアトラクション用にゲームも開発したっけ・・・)。そんなときに私が使っているのがMicrosoft F#という言語。面白い言語なんでMicorsoftも(そのユーザー数のわりに)力を入れてくれてはいますが、いかんせんやっている人が少なすぎる。なので、今後このブログを使ってちょくちょくF#の魅力を発信していきたいと思うのですが、最初はまず誰もやっていないF#の中で、さらに誰もやっていないF#でラズパイを動かすということを紹介していきます。とはいえ、我々は光無線通信が専門であって、プログラミングが専門ではないためそんな難しいことは書けませんが、そのなかでも「F#でも色々できるんだぞ」ということをアピールできたらと思います。(そんなもんQiitaに書けと言われるかも知れませんが、あれを読む人はすでに詳しい人ですので・・・)

Raspberry Piと.NET

開発担当の私が主にMicrosoft .NET Framework(以降.NET)を使っていたこともあり、当初はLEDバックホールにWindows PCを繋げて監視やデモを行っていました。.NETというのはMicrosoftの開発フレームワークのことです。プログラミングしたことない人にとって.NETはちょっと分かりにくい概念で、いつも説明に困りますが、ざっくりというと「C#(シーシャープ)やVisualBasic.NET(以下VB.NET)などいくつかの言語で簡単にコードを書くための開発環境」です。.NET上で動く各言語は、使っている「単語」はほぼ一緒で「文法」だけが異なるという構造になっています。そのため、どの言語で書いても互換性があるように作られています。例えばC#で書いたコードをVB.NETに変換するようなサイトも存在します。また、動作が書いた言語に依存しないため、VB.NETで書いたライブラリを、C#から参照するといったことも可能です。そして、.NETで使う「単語」は誰でも書きやすいように簡単になっていて、とても書きやすく、また様々な機能を少ない行数で書くことが出来るようになっており、私を含めWindowsのビジネスアプリ開発者の多くは.NETで開発しています※1

話をPCに戻します。LEDバックホールという通信機と一緒に動かすために、現場に置いておける、放置できるような小型なコンピューターがあると便利なわけですが、皆様もよくご存じの通り、いくら小さいPCといってもWindows PCだとそれなりの大きさもありますし、扱いも繊細さを求められます。しかもノートPCやWindowsタブレットなどディスプレーの付いたものは結構高額。また、Windowsだと会社での情報セキュリティ上の扱いも極めて面倒。そんなわけでWindows PCは現場で使う様な用途に向いていないのです。しかし、非Windowsであれば小型で扱いやすいコンピューターというのはいくつか存在していて、その中でもっとも有名なのがRaspberry Pi(ラズベリーパイ、以降ラズパイ)です。イギリスの慈善団体(ラズベリーパイ財団)が開発した教育用の小型コンピューターです。タバコの箱程度のサイズで軽量。それでいてUSBもイーサもWi-Fiも積んでいるし、入出力ピンがありセンサーなどいろいろな外部機器も繋げられます。そして最大の特長はなんといっても価格。昨今の半導体不足で値段が上がっていますが、それでも最新のものですら本体1万円以下と、他のPCより圧倒的に安いのです。そのため、教育用としてだけでなく、IoTや電子工作でも用いられ、それら分野ではすでに圧倒的なシェアを誇ります。ただ、ラズパイを使えば現場の問題は解決するのだろうと思いつつも、私は.NET開発者であるために数年前まではなかなか手が出せませんでした。なぜかというと、ラズパイのアプリを開発するにはPythonという言語を用いることが一般的で、そのままだと.NETでは動かせなかったからです。

fig.1
図1: Raspberry Pi (3B)

ラズパイで.NETを動かすためには、OSを標準のラズビアンOSからわざわざMicrosoftが作ったWindows IoTというOSに乗せ替える必要がありました。そして、このWindows IoTというOSがなかなか「残念な」出来。殆ど何も出来ない※2のにもかかわらず、不安定であり得ないぐらい重い。起動の失敗も多いですし、アプリ開発のためのリモート接続も安定しません。ラズパイ標準のOSは(教育用なので当たり前ですが)普通のLinux系OSとしてブラウザも普通のアプリも色々と動き、かつ安定しているのに、それをわざわざ消して、単体では何も出来ない、安定して起動すらしないOSを入れるというのは・・・ いや、そもそも起動に何分(下手したら10分)もかかるようなOSのどこがIoTなんだよ! 一時期Windows IoTでの運用も試してみましたが、あまりに困難が多く断念せざるを得ませんでした。

しかし、そんな残念なMicrosoftのOSチームとは異なり、Micorsoftの言語開発チームは素晴らしい対応をしてくれました。なんと.NETをラズパイでも動かせるように、.NETの方をラズパイに対応するようにしてくれたのです!それが、.NET Core※3というやつです。.NET Coreはこれまで、Windowsファミリーでのみ動くように開発されていた.NETをWindowsだけでなくラズパイ(Linux)、Mac OS、AndroidといったマルチOSで動くように変更したものです。この辺の経緯はいろんなところで書かれていますから検索して頂くとして、我々開発者側としては、これまで.NETではWindows用アプリしか開発できなかったのに、コードはそのままでラズパイやiOS、アンドロイドなど様々なOS向けにも開発できるようになったわけで、これは「ありがたい」と言うしかありません。私も.NET Coreの登場により、ついにラズパイ用の開発に手が出せるようになりました。

F#とはなにもの?

.NETは2002年にリリースされたもので、もともとC言語系の「C#」とBasic系の「VB.NET」の二つの言語に対応していました。どちらもオブジェクト指向言語※4として開発されたものです。オブジェクト指向言語は今もプログラミング言語の主流であり、C#やVB.NET以外にも、「Java」やiPhoneアプリ開発でおなじみの「Object-C」など多くのプログラミング言語がこの方式です。一方、F#はそれらとは異なる関数型言語に分類されています。関数型言語は、概念こそ古くからありましたが実際に普及したのはオブジェクト指向言語よりかなり後になります。.NETがリリースされた2000年初頭にはあまり使われていなかった言語で、そのためF#が出来たのも.NETがリリースされてから大分経った2010年のことになります。F#はもともとあった関数型言語(OCaml)をベースにして.NETに合うように改造された言語ということもあり、「Microsoftが.NETに関数型言語を追加するために改造された言語」という言い方もできると思います。

さて、何度も連呼した「関数型言語」という言葉ですが、そもそも関数型言語とは何でしょうか?オブジェクト指向言語では全てがオブジェクトで構成されるのに対し、関数型言語では全てが「関数とその答え」で構成されるプログラミング言語です。といっても言葉で説明するより見た方が早いでしょうから、簡単な例を出そうと思います。

  • AがBより大きければCに”big"という文字列を入れる、そうで無ければCに"small"という文字列を入れる

これをまずオブジェクト指向言語であるVB.NETで書いてみたいと思います。わかりやすくするためAとBは既に別な場所で宣言されているとします。(尚、VB.NETとVBAとほぼ同じ文法です)

Dim c As String = "" ' Cを宣言 ' 条件によりCに代入する値を変える If a > b Then c = "big" Else c = "small" End If Console.Write(c)

VB.NETではCという文字列(オブジェクト)を宣言して、IF文によって条件分岐し、場合によってCに"big"か"small"を代入するという構文になっています。つまり「Cを宣言して、この値を変化させる」ことをやっています。

これをF#で書くとこうなります。

//値を評価するための関数を宣言 let hyouka x y = if x > y then "big" //IF文の戻り値1 else "small" //IF文の戻り値2 let c = hyouka a b // Cに先に宣言した"hyouka"の答えを代入 Console.Write(c) // **このように直接入れることも出来ます** let c = if a > b then "big" else "small"

VB.NETのDimとF#のletは同じようなもの(宣言)だと考えてください。F#ではまずxとyを引数に持つ"hyouka"という関数を先に作っています。VB.NETのIF文は処理を行うための条件分岐ですが、関数型言語であるF#にとってのIF文は何らかの戻り値を返す関数です。そのため、"hyouka"という評価するための関数は中身がIF文しかないにもかかわらず戻り値を発生させます。そして、次の文で、Cは「hyouka関数にAとBの引数を入れた結果である」という宣言を行います。そのため、Cにはbigかsmallの文字列が代入されます。IF文は関数扱いなので、例の最後にあるように、Cに代入する関数として直接書くことも可能です。

VB.NETに限らずオブジェクト指向の言語では、「オブジェクトの型を宣言する」「オブジェクトに対して何かの変更を加える」という行為の繰り返しですが、F#の様な関数型言語では関数を繰り返して構成する事になります。関数型言語の何がメリットかというと、「何が起こっているのかがわかりやすい」ということです。例えば上の例で言えば、VB.NETはcはどこの行でも変更される可能性があるため「cという変数がどのような状況にあるのか」というのはコード全体を実際に実行されるであろう順番通りに追って見なければ分かりません。この「実行されるであろう」というのがくせ者で、実行される、されないまで考慮しなければいけないため、ここにエラーが潜む可能性が高くなります。一方F#であればcは関数でしか表現されないため、cがどんな値を取るかは、コード全体を見なくとも、関数を順に遡れば追っかけられるようになっています(図2)。

fig.2
図2: オブジェクト指向と関数型言語の違い

例えば、F#においてCの型を宣言していませんが、遡っていけばCに入るのは"big"か"small"のいずれかの文字列と分かっているので、Cは自動的に文字列と見なされます。これは、コードを実行せずとも、コードを書いている最中に通常ツールが自動的に行ってくれます。当然、それができるのも簡単に遡って取り得る値を判別できるためです。関数型言語のそのような性質※5により、非常に読みやすく、途中で何が起こるのかが分かりやすいコードになるため、バグの発生が抑えられます。私が普段F#を使っている理由もこれに尽きます。

ただし、F#は純粋な関数型言語と言うわけではなく、.NETとしての機能も持ち合わせます。つまり、オブジェクト指向言語として書くことも可能と言えば可能です。つまり、関数型言語とオブジェクト指向言語の両方を併せ持つ言語であり「マルチパラダイム言語」という呼ばれ方もしています。例えば下の様にVB.NETと同じような書き方も出来なくはありません(普通しませんが)。

let mutable c = "" //ここでCを変更可能な変数(オブジェクト)として宣言 // このIF文はunitを返す if a > b then c <- "big" // オブジェクトに値を代入 else c <- "small" // オブジェクトに値を代入 Console.Write(c)

ここでのIF文は何も返さないように見えますが、F#においては、例えばオブジェクトに値を代入するとかメソッドなど、一見なにも返さないような文でも、形式的に「unit」という戻り値を返すことになっています。unitに意味は無いため、無視して構いません。上の例では、IF文の中の「C <- xx」という代入文がunitを返すため、IF文としてunitを返しています。また、「Console.Write(C)」という表示するためのメソッドもunitを返します。この辺は、純粋な関数型言語ではない.NETとしての性質ももつF#ならではだと思います。つまり、C#やVB.NETと同じ事を、関数型でもできる言語、これがF#というわけです。

おわりに

というわけで、今回は.NETとF#の説明だけに終わってしまいましたが、次回以降はこんな変わった言語であるF#を使って、どのようにラズパイを動かすのかを説明していきたいと思います。


※1; ゲームなど高速動作が問われるアプリケーションにおいては、今でもC++などの「書きにくいけど軽量」な言語を使っています。

※2; Winodws IoTには、GUIやコマンドプロンプト等がありますが、OSのみだと極々限られた機能しか動かない、いわゆる組み込み用OSです。通常は何か特定の(単一の)アプリを動かした状態で使うことが想定されています。尚、Windows IoTには「エンタープライズ」という普通のWindowsに近いちょっと豪華なバージョンもありますが、x86(x64)系CPU用で、ラズパイ(ARM)では動きません。

※3; .NET Coreの登場により、Windows専用の.NETを「.NET Framework」、マルチOSの.NETを「.NET Core」と呼び分けていましたが、2020年に.NET Frameworkを廃止し、.NET Core側のみが生き残ることになりました。呼び名も.NET + <メジャーバージョン番号>に統一されて、2022年1月現在の最新バージョンは.NET6と呼ばれています。

※4; すべての変数、関数、メソッドが「オブジェクト」として扱われる言語です。例えば、C#やVB.NETにとっては、整数(integer)も文字列(string)も、プロシージャもすべて「オブジェクト」として扱われます。

※5; 一方で、この性質によりF#は。NETの他の言語ではあり得ないほど「型に厳密」な言語になっています。C#やVB.NETであれば、Double型にinteger型を代入すると、当たり前のようにInteger型をDouble型に変換してくれますし、アップキャストであれば結構柔軟にやってくれますが、F#ではそんなこと絶対にしてくれません。