Closed5

NeovimでOCaml

こまもか🦊こまもか🦊

paru -S ocaml
v5.0.0を入れる。READMEに結構色々変更入ってるとか書いてあったけどこれが始めてなので気にしない。

MasonでLSPをインストールしようと思ったけど、opamとかいうOCamlのパッケージマネージャ?が無いと怒られたのでインストール

paru -S opam

それでも無いと怒られた。調べたところopam initすれば良いらしい。あとなんかおかしくなったら~/.opamを削除すれば良いらしい。こういうゴリ押し出来る仕様はとても好き。
https://qiita.com/zenwerk/items/7bc6177adcbeb6990e60

Do you want opam to modify ~/.config/fish/config.fish? [N/y/f]
(default is 'no', use 'f' to choose a different file)

と聞かれたのでYesと答える。...と思ってEnterを押したらデフォルトでNoだったらしく、手動でsource /home/coma/.opam/opam-init/init.fish > /dev/null 2> /dev/null; or trueを実行する。

シェルを再起動して再びMasonを実行。今度は良い感じ。

こまもか🦊こまもか🦊

LSPをインストールしている間に基本的な文法を調べてみる。(事前情報がCoqと関数型くらしかない)

https://otiai10.hatenablog.com/entry/2015/10/26/215351

print_string "Hello World";;

で行けるらしい。セミコロン2つなのはなんか新鮮。
処理系の話とか気になる(主に実行速度の面で)けどあんまり書いてない...セルフホスティングしてるっぽいし、多分直にバイナリになる感じなのかな。

OCAML入門にduneっていうビルドツールを使うのがオススメと書いてあったのでインストール。
https://github.com/ocaml/dune

dune init project NAMEでプロジェクトを新規作成出来るらしい。便利。

とりあえず実行する前にdune build @installで依存関係をインストールしてみる。

完了したらdune execとかいうもっともらしいコマンドを実行してみる...けどエラーになった。
ファイル名とか指定してみたけど何も出力されないのでプロジェクト名を入れてみた。

無事実行された。

こまもか🦊こまもか🦊

せっかくなのでなんか作ってみる。

シャワー浴びてる間に定期的にHTTPリクエストを送信するコマンドがあるとAPI開発が捗りそうなのでそれを作ってみたいと思う。

OCamlでHTTPする際にはcohttpというライブラリが良いらしい。
opamでインストール

opam install cohttp-lwt-unix cohttp-async

パッケージをインストールしていて気が付いたんだけど、OCamlのビルドは結構遅い。Rustと良い勝負じゃないかと思う。(多分Rustの方が速い)
これはHaskellとかもそうだったので関数型言語の宿命みたいなものなのかもしれない。

opamでインストールしてもLSPでエラーが出てしまうので色々調べた。
その結果、なんとduneでは使用するライブラリをduneS式を使って指定する必要があるらしい。(Lispやっといて良かった~)

とりあえずCoHttpのサンプルコードを実行するために書いたduneファイル

(executable
 (public_name ocaml)
 (name main)
 (libraries ocaml cohttp-lwt-unix cohttp-async lwt_ssl))

(opensslをインストールしてからじゃないとエラーが出るそうなので要注意。参考)

これはopam installを実行してから書く必要がある。
ちなみに、duneでプロジェクトを作った際にopamのインストール先がローカルになるようなので、グローバルインストールしてバージョンが~みたいな事は心配しなくて良さそう。とても良き。

dune exec ocamlで無事実行された。RedditのHTMLとレスポンスボディを取ってくるコードらしい。

こまもか🦊こまもか🦊

環境も整った事なので早速先述したプログラムを解読していく。
プログラム全文をここにも貼ってみる。原文はCohttpのREADME

open Lwt
open Cohttp
open Cohttp_lwt_unix

let body =
  Client.get (Uri.of_string "https://www.reddit.com/") >>= fun (resp, body) ->
  let code = resp |> Response.status |> Code.code_of_status in
  Printf.printf "Response code: %d\n" code;
  Printf.printf "Headers: %s\n" (resp |> Response.headers |> Header.to_string);
  body |> Cohttp_lwt.Body.to_string >|= fun body ->
  Printf.printf "Body of length: %d\n" (String.length body);
  body

let () =
  let body = Lwt_main.run body in
  print_endline ("Received body\n" ^ body)

let body =

以下の処理の内容をbodyに束縛している。

Client.get (Uri.of_string "https://www.reddit.com/")

これ単体で「文字列をURL形式に変換してGETリクエスト」を行っているっぽい。

>>= fun (resp, body) ->

急に見慣れない記号が出てきたけど、関数型言語あるあるのパイプ亜種みたいなものなのかな。
これで先述のClient.get()の戻り値が(resp, body)であるという事が分かる。ノリとしてはJSのArrow Functionに近い...?

let code = resp |> Response.status |> Code.code_of_status in

codeは多分HTTPステータスコード。fun() ->で末尾が->なことから多分この行から関数のブロックに入ってるっぽい...と思ったけど戻り値の定義な可能性が捨てきれないからChatGPTに聞いてみたら戻り値の型らしい。なんでletが使えるんだ...?と思ったのでそれも聞いてみたところ、一時的に結果を格納する為にletを使うことが出来るらしい。

つまり、

  • ->は関数の戻り値を値を表していて、
  • 明示的に型を表記せず、
  • 一時的に値を代入している

と上のような書き方になるっぽい。

in ~

let in~と書くことで複数行の処理の結果を変数に束縛する事が出来る。JSで言う即時関数に近いものを感じる。

 let code = resp |> Response.status |> Code.code_of_status in
  Printf.printf "Response code: %d\n" code;
  Printf.printf "Headers: %s\n" (resp |> Response.headers |> Header.to_string);

  body |> Cohttp_lwt.Body.to_string >|= fun body ->
  Printf.printf "Body of length: %d\n" (String.length body);

  body

|>は関数型言語おなじみのパイプ処理。わざわざこんなスクラップを見る人は既に知ってると思うのであんまり解説しない。

Printf.printf()はおなじみ標準出力をする関数。OCamlは副作用を許容する仕様なのでこういう副作用が発生する関数もカジュアルに使える。

最後のbodyでreturn相当の処理をしている。つまりこのbodyの結果が最初のlet body =...の変数に束縛される。

let () =

ChatGPT曰くユニット型(unit type)を持つ式を実行するために使われる特殊な構文で、副作用を持つ関数を実行するために必要らしい。ChatGPT曰く処理が順番に実行されるらしいので非同期云々をしたい時はまた別のアプローチがありそう。

このスクラップは2024/02/12にクローズされました