独学大学情報学部

ただのノート。主にプログラミング。

「Scalaに存在演算子を求めるのは間違っているだろうか」をLens/Prismで解いてみる

元の記事はこちらです。

Scalaに存在演算子を求めるのは間違っているだろうか - だいたいよくわからないブログ

TL眺めてたらがくぞさんが

ってpostしてて「あ!このデータ構造、Lensのサンプルで見たことあるやつだ!」 が再生されたので(?)、やってみました。

もちろんScalaでLensといえばMonocleだよね!!*1

前置き

その1

何も考えずに各case classに対してLensを定義してやってみます。

ちなみに、&<-?applyPrism^|->composeLens^<-?composePrismエイリアスメソッドです。

このsomemonocle.std.option#someで、Monocle標準で提供してるPrismです。 b.valueOption[A]なので、a.valueする為にOption[A] => Aが必要だったので使用しています。

結果は以下の通り。

scala> Example1.good
res0: Option[Int] = Some(1)

scala> Example1.bad
res1: Option[Int] = None

良さそうですね。

その2

some分冗長な気がしたので、_b_dをPrismにしてみます。

これでsomeがなくなった分短く書けるようになりました。 もちろん、同じ結果が得られます。

scala> Example2.good
res2: Option[Int] = Some(1)

scala> Example2.bad
res3: Option[Int] = None

まとめ

どちらにせよCoffeeScriptっぽく書けるわけでもなく、@xuwei-k さんの

「Scalaに存在演算子を求めるのは間違っているだろうか」の解答例 - scalaとか・・・

val x: Option[Int] = OptValue(edcba).wrap._value._fuga._hoge._bar._foo

みたいな感じでOptionを意識することなく繋げることは出来ませんでした。 とはいえ、macroもType Dynamicも使ってない*2ので、それなりにシンプルにかけてるはず...

もっと短くカッコよく書ける方法があれば教えて下さい!!

おまけ

コードの表現的に負けてしまった(?)ので、Lens/Prismを使ったメリットを挙げておくと

のようにgetだけでなくsetやmodifyもできるようになります。やったぜ!

*1:Scalazにもshapelessにもあります。

*2:Monocle側のGenLensはマクロでLens生成してますけどね

ScalaのStream#filterNotが壊れてた件

既知のbugのようですが、知らずに1時間無駄にしてしまって激おこなのです!!(

先日2.11.7が出ましたが、修正が入るのは2.12-M2っぽいので暫くこのままですね。 [SI-8627] Stream#filterNot broken, should be overridden in Stream - Scala

手元でもすぐに確認できるので、皆さんも是非ハマってみてハマらないようにしてくださいね!

Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_25).
Type in expressions to have them evaluated.
Type :help for more information.

scala> Stream.from(1).filter(_ > 5)
res0: scala.collection.immutable.Stream[Int] = Stream(6, ?)

scala> Stream.from(1).filterNot(_ > 5)
// そして、返答はなく、只々MBPのCPUファンが唸りを上げるのみ

   

追記: あの悲しき2.11.3事件の原因だった模様。。。情報ありがとうございます!

MonocleのapplyLensメソッドの使い方

去年末くらいからぼちぼち触り始めてたけど今更知った。

使い方

例えばこんな感じのデータ構造(とLens)が定義されてるとして、*1

@Lenses case class Address(street: String, postalCode: String)
@Lenses case class User(name: String, age: Int, address: Address)

Lensを合成していってgetしたり、modifyしたりするわけですが、

import monocle.syntax.apply._
import Address._, User._ // 生成されたLensの為に

val me = User("Aoino", 24, Address("street", "xxx-xxxx"))

scala> address composeLens street get me
res1: String = street

// ^|-> はcomposeLensのエイリアスメソッド
scala> (address ^|-> street).modify(_.capitalize)(me)
res8: User = User(Aoino,24,Address(Street,xxx-xxxx))

applyLensを使うと以下のようにかけます。

scala> me applyLens address composeLens street get
res13: String = street

// &|-> applyLensのエイリアスメソッド
scala> me &|-> address ^|-> street modify(_.capitalize)
res16: User = User(Aoino,24,Address(Street,xxx-xxxx))

使わなかった時よりも若干見やすく、流れるようなインターフェース感()が増しましたね。おつおつ。

実装

単純に暗黙的変換でメソッドが追加されてる(ように見える)だけです。

https://github.com/julien-truffaut/Monocle/blob/v1.1.1/core/src/main/scala/monocle/syntax/Apply.scala#L10-L19

ちなみに

タイトルにはapplyLensって書いてますが、ほかにも以下のメソッド共が存在します。

method aliase
applyTraversal &|->>
applyOptional &|-?
applyPrism &<-?
applyLens &|->
applyIso &<->

で、一応composeXXXもまとめると

method aliase
composeTraversal ^|->>
composeOptional ^|-?
composePrism ^<-?
composeLens ^|->
composeIso ^<->

初見は「はぁ???」ってなりますが、composeと一緒に眺めると&か^の違いしかないので、覚えるのはそんなに苦労しないんじゃないかと。*2

まとめ

ってなわけで、これでますますScalaでLens欲が高まりますね!!

*1:マクロアノテーションの力により、コンパニオンオブジェクト内にcase classで定義されてるフィールド名と同名のLensが定義されます。詳しくはこちら

*2:とはいえ、右手onlyで打つ記号共なので逆に打ちにくいっていう

Lens&Prism勉強会でMonocleについて発表してきた

とっても今更なのだけど、記録として。

connpass.com

↑にLT枠で参加してきた。

資料は

www.slideshare.net

色々あって、前半部分参加できなかったのだけれど、TL眺める限りとても濃い話だったっぽいので、ちょっと残念。

http://togetter.com/li/828498

当日は予想以上の人で、緊張のため話そうと思ってたこと半分くらい飛んじゃって、テンパって足プルプルしてたけど、まぁなんとかなって良かった。

今回の発表内容は「理論はよく分からないけど、使い方分かってきたから実践で使ってみた」的な話だったので、もし次回があるのなら今度は理論的な話とかもできるようになりたいなーとか思ってます。

当日の準備、懇親会、片付けに一切参加できずですんませんでした、、、
また機会があれば、今度こそお手伝いできればと!

みなさんお疲れ様でした!ありがとうございました!!

「Scala勉強会第143回 SPECIAL DAY ハッカソン in 歌舞伎座」に参加してきた

今日は ↓ に参加してきました。
http://rpscala.doorkeeper.jp/events/20374

一番後ろの席に座ってたので人数数えてみると、ざっと30人弱くらいでした。
各々自己紹介の後、ハッカソンスタート。

今日やったこと

スライド作るの面倒だった時間なかったので、発表内容もここでまとめます。

↓今日はこれの開発してました。
https://github.com/aoiroaoino/connpasscala

ざっくり言うとConnpassってイベント支援サービスのAPIクライアント for Scalaです。既に返ってきたJsonをパースする部分は作っていたので、今回はHTTPリクエスト投げる部分+αを実装しました。 HTTPリクエストにはscalaj-httpJsonのパースにはargonautを使用しています。 特にこだわりがあったわけではなく、単純に使った事なかったので試したかったってのが理由。

使い方は以下のような感じです。(2015/02/12 現在)

scala> import connpasscala._, Connpasscala._
import connpasscala._
import Connpasscala._

scala> :paste
// Entering paste mode (ctrl-D to finish)

Client()
  .keyword("scala")
  .ownerNickname("AoiroAoino")
  .run()

// Exiting paste mode, now interpreting.

res0: Option[connpasscala.Response.Connpass] =
Some(Connpass(1,1,1,List(Event(10643,Scalive # 1.414 @ 西麻布ベース,Scalaなどについて語らいながら1.414人前の肉を喰らう@西麻布ベース,<h3>▼趣旨</h3>
<p>前回のScalive!<br>
...

scala> val event = res0.get.events.head
event: connpasscala.Response.Connpass.Event =
Event(10643,Scalive # 1.414 @ 西麻布ベース,Scalaなどについて語らいながら1.414人前の肉を喰らう@西麻布ベース,<h3>▼趣旨</h3>
...

scala> event.title
res2: String = Scalive # 1.414 @ 西麻布ベース

scala> event.hashTag
res3: String = scalive

基本的にはConnpass API仕様の検索クエリのキャメルケース名メソッドで検索条件を追加していって、run()で実行後、レスポンスフィールド名のキャメルケース名メソッドでデータにアクセスする感じです。

とりあえず動いている状態ですけど、エラー処理皆無だったりパッケージ構成変だったりテスト書いてないし依存関係切り分け出来てないし、etc...と、実用には程遠い状態orz ただまぁ、折角の(それなりに使えそうな?)初ライブラリなので、CIやらバージョン管理やらMaven Centra Repositoryへの公開やら、一通り試してみたいですね。

感想

今回は久しぶりのハッカソン参加かつ、進捗ダメじゃなくて発表まで行けたので良かった!!でもやっぱりスライドは作るべき。。なんとなく伝えきれなかった感ある。。。

(´-`).。oO(なんとか時間内に使える状態になって良かった...)

まとめ

主催のみなさん、参加者のみなさん、お疲れさまでした!ありがとうございました!