「Scalaに存在演算子を求めるのは間違っているだろうか」をLens/Prismで解いてみる
元の記事はこちらです。
Scalaに存在演算子を求めるのは間違っているだろうか - だいたいよくわからないブログ
TL眺めてたらがくぞさんが
Lens の匂いを感じる http://t.co/KGFPNSLSP2
— がくぞ (@gakuzzzz) 2015, 7月 15
ってpostしてて「あ!このデータ構造、Lensのサンプルで見たことあるやつだ!」 が再生されたので(?)、やってみました。
もちろんScalaでLensといえばMonocleだよね!!*1
前置き
その1
何も考えずに各case classに対してLensを定義してやってみます。
ちなみに、&<-?
はapplyPrism
、^|->
はcomposeLens
、^<-?
はcomposePrism
のエイリアスメソッドです。
このsome
はmonocle.std.option#some
で、Monocle標準で提供してるPrismです。
b.value
がOption[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もできるようになります。やったぜ!
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事件の原因だった模様。。。情報ありがとうございます!
@AoiroAoino これ http://t.co/xclriGFpXg の原因になったという意味で有名な(?)やつですね(という直接役に立たない余談)
— Kenji Yoshida (@xuwei_k) 2015, 6月 25
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))
使わなかった時よりも若干見やすく、流れるようなインターフェース感()が増しましたね。おつおつ。
実装
単純に暗黙的変換でメソッドが追加されてる(ように見える)だけです。
ちなみに
タイトルにはapplyLensって書いてますが、ほかにも以下のメソッド共が存在します。
method | aliase |
---|---|
applyTraversal | &|->> |
applyOptional | &|-? |
applyPrism | &<-? |
applyLens | &|-> |
applyIso | &<-> |
で、一応composeXXXもまとめると
method | aliase |
---|---|
composeTraversal | ^|->> |
composeOptional | ^|-? |
composePrism | ^<-? |
composeLens | ^|-> |
composeIso | ^<-> |
初見は「はぁ???」ってなりますが、composeと一緒に眺めると&か^の違いしかないので、覚えるのはそんなに苦労しないんじゃないかと。*2
まとめ
ってなわけで、これでますますScalaでLens沼欲が高まりますね!!
Lens&Prism勉強会でMonocleについて発表してきた
とっても今更なのだけど、記録として。
↑にLT枠で参加してきた。
資料は
www.slideshare.net
色々あって、前半部分参加できなかったのだけれど、TL眺める限りとても濃い話だったっぽいので、ちょっと残念。
当日は予想以上の人で、緊張のため話そうと思ってたこと半分くらい飛んじゃって、テンパって足プルプルしてたけど、まぁなんとかなって良かった。
今回の発表内容は「理論はよく分からないけど、使い方分かってきたから実践で使ってみた」的な話だったので、もし次回があるのなら今度は理論的な話とかもできるようになりたいなーとか思ってます。
当日の準備、懇親会、片付けに一切参加できずですんませんでした、、、
また機会があれば、今度こそお手伝いできればと!
みなさんお疲れ様でした!ありがとうございました!!
「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-http、Jsonのパースには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(なんとか時間内に使える状態になって良かった...)
まとめ
主催のみなさん、参加者のみなさん、お疲れさまでした!ありがとうございました!