たにしきんぐダム

プログラミングやったりゲームしてます

Scripting with Scala CLI

Scala Advent Calendar 8日目の記事です。

Scalaスクリプト書いてますか?僕は書いてません、でした、最近までは。

隣のクラスのPythonやGoはインタープリタコンパイラをインストールしたらすぐに適当なスクリプト書き始められるというのに、Scalaときたらちょっとしたスクリプト書くのにもsbtなりのビルドシステムをセットアップして〜みたいなことしないといけない。

そんなあなたにはこれ!

scala-cli.virtuslab.org

Scala CLIScala をサクッと書くための CLI ツールで、これさえインストールすればsbtなどのビルドツールを使わなくてもすぐに一通りのプログラムを書くことができるようになります。

インストール方法とかはドキュメントを見てもらって、Scala CLI 使ってなにか書いてみましょう。

scala-cli.virtuslab.org

ここ最近の為替レートのチャートを表示するスクリプト書いてみる

最近は円安すごくて為替レート気になりますよね。ということでCLIからサクッと為替レート見れるスクリプトを書いてみます。(とりあえずEUR->JPYで)

為替レートの取得には Exchange Rates API を使って、ライブラリは以下のものを使います。

できました。(secret に取得したAPIトークンを入れてね)

#!/usr/bin/env -S scala-cli shebang --quiet

//> using lib "com.softwaremill.sttp.client3::core:3.8.3"
//> using lib "com.mitchtalmadge:ascii-data:1.4.0"
//> using lib "com.lihaoyi::ujson:2.0.0"
import ujson._
import sttp.client3._
import com.mitchtalmadge.asciidata.graph.ASCIIGraph

import java.time._

val secret = "xxx"

val endDate = if (args.length > 1) LocalDate.parse(args(1)) else LocalDate.now()
val startDate = endDate.minus(Period.ofDays(90))
val client = SimpleHttpClient()
val response = client
  .send(
    basicRequest
      .header("apikey", secret)
      .get(
        uri"https://api.apilayer.com/exchangerates_data/timeseries?start_date=${startDate}&end_date=${endDate}&base=EUR&symbols=JPY"
      )
  )
val responseBody =
  response.body.getOrElse(throw new Exception("Service not responding"))
val data = ujson.read(responseBody)
val values =
  data("rates").obj.toSeq.sortBy(_._1).map((_, value) => value("JPY").num)
println(ASCIIGraph.fromSeries(values.toArray).plot())

これを script.sc として保存して以下のコマンドで実行。

$ scala-cli script.sc
# もしくは
$ chmod +x script.sc
$ ./script.sc

実行すると直近90日のEUR->JPYの為替レートが以下のような感じのascii chartで表示されます。

実行結果(うまくコピペできなかった)

解説

using lib なんちゃらって何?

//> using lib "com.softwaremill.sttp.client3::core:3.8.3"

using directive っていうやつ。//> using lib "org::name:version" でそのスクリプトからライブラリをダウンロードして使えるようにしてくれる。(裏側ではcoursierを使ってダウンロードされてivyキャッシュに保存されるので、実行するたびにダウンロードされることはないよ)。

scala-cli.virtuslab.org

どのバージョンのScalaが使われてるの?

デフォルトだと最新(今だと3.2.0)。

$ scala-cli -S 2.13.10 script.sc みたいな感じで Scala のバージョンを指定したり

//> using scala "2.13.10"sc ファイルの頭につけて、using directive でバージョン指定もできる。

IDE サポートは?

あります! Metals の場合は .sc ファイルを開くと、scファイルが見つかりました、ScalaCLI script としてインポートしますか? みたいなのが出るのでハイを押すと普通にIDE機能を使った状態でスクリプトを書くことができるようになる。

もしくは IDE Setup | Scala CLI

ammonite とは何が違うの?

裏側では ammonite 使ってます(それにいろいろ便利機能をラップした感じ)。なので ammonite でできることはだいたいできるはず。

ammonite でできること。

blog.3qe.us

なんで ./script.sc で実行できるの?

shebang#!/usr/bin/env -S scala-cli shebang って書いてるから。

https://scala-cli.virtuslab.org/docs/guides/scripts#self-executable-scala-script


これで Scala でもシュシュッとスクリプト書けるぞ!!!

良いブログ

alexn.org

ちなみに

(ちなみに今後 scala コマンドをこの scala-cli で置き換えるという SIP が提案され、最近 accept されました)。

contributors.scala-lang.org

contributors.scala-lang.org

効率の良い学習方法を身につけるために『使える脳の鍛え方 成功する学習の科学 』を読んだ

2-3年ほど前に大学院受験の勉強を始める際に読んだ。中高生のときに多分こうすると良いんだろうなという勉強方法(というか記憶術)を裏付けてくれる良い本だったのを覚えている。

最近人に何度かおすすめすることがあり、パラパラっと読み直したけどやっぱり良い本だった。

この本はここ百数十年くらいの効率の良い学習方法(この本でいう「学習」とは主に学んだことの長期記憶への定着を指す)に関する研究結果をまとめて、認知心理学や教育科学の門外漢に対しても分かりやすく噛み砕いて書かれたもの。

目から鱗の真実、のようなことは書かれていなくて、まあそうだよねっていうような学習方法が効率の良い/悪い学習方法だということが、過去の心理学実験の結果をベースに書かれている。

書かれている話はだいたいこういう感じ。

  • 多くの学生は大事な部分をマーカーで印をつける、似たような本を何度も多読する、短い期間に集中して勉強するなどの学習方法を好むが、それらの学習方法はさほど効率的ではない
  • 定期的に学んだことを思い出そうとすることで長期記憶の定着が促進される
  • 一種類の問題をマスターして次、と学習を進めるのではなく、多様な問題を交互に学習するほうが効果的
  • すぐに答えを見るのではなく、どうすれば良いのか考えると良い
  • 新しい知識を、すでに自分の知っている知識や体験、異なるコンテキストに結びつける

この分野のサーベイ論文を門外漢向けに大衆化しましたみたいな内容で読み物として面白かった。

本読むのが面倒な人は productivity 大好き Youtuber の Ali Abdaal さんがほとんど全部説明してくれてるので以下の動画を見るのがおすすめ(というかこの人の動画からこの本を知ったのであった)

youtu.be

sbt-npm-package でシュッと scala.js のビルド成果物を npm にアップロードする

Scala Advent Calendar 2022 - Qiita 1日目の記事です

scala.js でビルドした成果物を npm にアップロードすることありますか? 私はある。

普通に scala.js でビルドした js を npm にアップロードしようとするとこういう感じの流れになります。

  • sbt fullOptJS とかで js にビルド
  • ビルド成果物(.../target/scala-2.13/foo-opt.js とか) を適当なディレクトリにコピー
  • そのディレクトリに package.json と、npmにアップロードする README.md を設置
  • npm login して npm publish

まあ、これでも別にいいっちゃいいのですが、いくつか気に入らないところがあり

  • build.sbt の設定と、package.json の設定を別々に管理することになる (version とか)
  • sbt によるビルド成果物のパスをリリーススクリプト側にハードコードしないといけない。
    • 別にいいけど、なんとなくビルド成果物の居場所はsbtが知ってる状態であってほしい。

なんか sbt 側で npm publish をラップしたいな〜と思って適当に github 眺めてると良さげな sbt plugin を見つけました。

github.com

全然ドキュメントない!けどコード読んだらなんか良さそうだったので使ってみた

sbt-npm-package ざっくり

sbt-npm-package がやってくれるのは

  • npm-package ディレクトリを target 以下に作る (target/scala-2.13/npm-package とか)、ここにjsとかpackage.jsonとかを集めてくれる。
  • npmPackageNpmrcnpm-package/.npmrc の生成
    • 中身はデフォルトで //registry.npmjs.org/:_authToken=${NPM_TOKEN}
  • npmPackagePublish で js 成果物を npm にアップロード
    • npm-package/package.json を build.sbt での設定を元に生成
    • ビルド成果物のJSを npm-package/main.js にコピー
    • (デフォルトではプロジェクトルートの) README.mdnpm-package にコピー
    • npm-package ディレクトリ上で npm publish を実行

なので基本的にはこの記事の最初に述べたリリース手順をsbt側に寄せて自動化してくれるというシンプルなもの。

設定しそうな settingKey

sbt-npm-package/NpmPackagePlugin.scala at f72e90581a63841039685371dbbe46e20e6ae6c7 · davenverse/sbt-npm-package · GitHub

settingKey 説明 デフォルト
npmPackageName package.jsonname sbt の project name からいい感じに作られる
npmPackageDescription description
npmPackageVersion version sbtversion が利用される
npmPackageRepository repository git ls-remote --get-url origin
npmPackageREADME アップロードする README.md へのパス README.md
npmPackageStage fullOptJS とか fastOptJS とか指定する FastOptJS

設定にない項目は npmPackageAdditionalNpmConfig で書き加えられる。

npmPackageAdditionalNpmConfig := Map(
   "homepage" -> _root_.io.circe.Json.fromString("https://scalameta.org/")

使用例

例えば

npmPackageName := "scalameta-parsers",
npmPackageDescription := "Library to parse Scala programs",
npmPackageRepository := Some("https://github.com/scalameta/scalameta"),
npmPackageAuthor := "scalameta",
npmPackageLicense := Some("BSD-3-Clause"),
npmPackageKeywords := Seq("scala", "parser"),
npmPackageVersion := "4.6.0",
npmPackageAdditionalNpmConfig := Map(
  "homepage" -> _root_.io.circe.Json.fromString("https://scalameta.org/")
),

こんな感じの設定で npmPackagePublish すると以下のような pakcage.json が作られます。

{
  "name" : "scalameta-parsers",
  "description" : "Library to parse Scala programs",
  "version" : "4.6.0",
  "type" : "commonjs",
  "repository" : {
    "type" : "git",
    "url" : "https://github.com/scalameta/scalameta"
  },
  "author" : "scalameta",
  "license" : "BSD-3-Clause",
  "main" : "main.js",
  "dependencies" : {},
  "devDependencies" : {},
  "keywords" : ["scala", "parser"],
  "homepage" : "https://scalameta.org/"
}

良さそうですね。

あとは repository secret に npm からとってきた access token を NPM_TOKEN として登録して、以下のように npm と sbt のある環境で npmPackageNpmrc -> npmPackagePublish すれば出来上がり

name: Release JS
on:
  push:
    tags: ["*"]
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v3
        with:
          node-version: "18"
      - uses: actions/checkout@v3
      - uses: olafurpg/setup-scala@v13
      - run: git fetch --unshallow
      - name: Publish
        run: sbt "parsersJS/npmPackageNpmrc; parsersJS/npmPackagePublish"
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

詳しくはこちら

github.com

ブログに使い方書かないで sbt-npm-package にPRでもすれば良かった気もしてきた。

一人旅が下手

自分はもっと一人でいろいろ楽しめるタイプの人間だと思っていた。しかし、これまで何度か海外に一人で旅行に行ってきて、また2022年9月にもひとりでポーランドに出張/旅行に行ってきて確信したのだが、自分は思ったより一人旅が下手なようだ。

  • キレイな景色とか美味しい料理を一人で食べても、その感動をシェアできる人がいないと感動を噛み締められない。
    • すぐ興味が別のものに行くので、キレイだなぁと思っても10秒くらいで飽きてしまう。
  • あまり計画立てたりアクティブにどこか行くタイプでもないので、海外に滞在しても周囲のスーパーとかカフェとか手近なところをめぐるだけ
    • 現地の文化や空気を味わうのが好きなのでこれも楽しい
  • なんでもないカフェやバーとかで突然知らない人と会話を楽しめるような社交性はない。

逆に自分はどういう旅行に楽しみを見出していたかというと、人との出会いに一番の楽しみを感じていたなと思う。

例えば

  • ポーランド旅行記 - たにしきんぐダム
    • ScalaDays 2019 に参加するためにスイスのLausanneに旅行に行った際はは OSS つながりで Ólafur Páll Geirsson や現職の同僚である Tomasz Godzik と話せたし
    • ソーシャルイベントのおかげで全く接点のなかった人と仲良くなったり、カンファレンスで会った日本人の方からのつながりでドイツの会社のエンジニアの人と知り合うことができた。
  • コロナ禍2022年9月海外渡航メモ - たにしきんぐダム
    • 今年の2022年9月にポーランドのKrakowに旅行に行った際は、そもそも会社の同僚とのソーシャライズが主目的だったのでいろんな人とのコミュニケーションが取れてよかった
    • また sphere.it というオフラインカンファレンスでも、友人が何人か参加していたのでそのつてでネットワークを広げることができた。
  • シドニー旅行記 - たにしきんぐダム
    • 一人旅ではないが、8年くらい前にシドニーに語学留学したのが人生で一番楽しい海外滞在だったと思う。
    • 思い出補正や、シドニー自体が魅力的な街であること、初めての海外というのもあるが、やはりクラスメイトと一緒に留学したことや、現地の大学生や別の日本の留学中の学生と仲良くなれたのが一番大きかったんじゃないかなと思う。
      • 今度またシドニーに行って検証したい気もする。
  • ローザンヌ旅行記 - たにしきんぐダム
    • 一方3年前にKrakowに旅行に行った際は、知り合いも全然おらず、街歩きは楽しいけどすぐ飽きてしまい、滞在中数日間はAirbnbの宿にこもってゼルダで遊んでいた(最悪)

ということで

  • (知り合いの参加している/自分が発表する)技術カンファレンス駆動旅行は、知らない人とコミュニケーションを取る機会があるので良い
  • (少人数の)ソーシャライズイベントが用意されていると最高
  • 知り合いのいない場所に一人で(海外)旅行してもどうせ楽しめないのでやめておく
    • 国内旅行も同じではあるけど、国内旅行はスパンが短いので飽きる前に帰ってこれているので良いのかもしれない。
  • 複数人で旅行だと特にイベントなどのない旅行も楽しめるかもしれない
    • けど友達との旅行なら別に海外じゃなくてもどこ行っても楽しいんじゃないか?

思ったより自分は社交的な人間なのかもしれないね。

午前研究して午後仕事しようと思ったけど全然うまくいってない

東工大大岡山キャンパスの枯れ木と曇り空

2022年10月から大学院に復学して、大学院生活とヨーロッパの会社にリモートで働く二足のわらじをやっている。

日本と本社との時差は8時間(サマータイム中は7時間)なので、基本的にチームメンバーが仕事始めるのは日本時間の夕方以降。昼過ぎや昼前からぼちぼち仕事はじめて夜10過ぎくらいまで仕事をする生活をしている。

そんな感じなので午前中は大学のことやって、仕事は午後やるようにすれば完璧では???と思って大学院に復学してみたのだけれど...全然うまく行ってない。

そもそも今の仕事も研究もあまりスキマ時間でできるような感じになっておらず、午前中に研究っぽいことと行ってもちょろっと論文読んで研究した気分になってるだけ。あとは土日のどちらかにちょろっとすすめるくらいで全然進捗は出ていなくて、結局仕事ばっかりしてしまっている。(あと復学決めた直後に会社で締め切り厳し目なプロジェクトを任されてしまったのも問題かもしれない)。

自分はコンテキストスイッチそんな得意じゃない(得意な人間がいるのか???)し、仕事+研究で週7日10時間労働できる鉄人でもないので、やっぱりこの日は研究デーみたいなの作ってあげないとだめな気がしてきた。

稼働週4にして月か金は研究デー+土日のどちらかも研究デーってことにしようかなぁ...

仕事も大学も楽しくはあるので贅沢な悩みである。