【一人 Ruby Advent Calendar 2017】ginza.rb で Ruby 2.5 の話を聞いてきた【21日目】
一人 Ruby Advent Calendar 2017 21日目の記事になります。
ちょっと話が前後してしまいますが、火曜日にginra.rb に初参加してきたのでそのまとめを。
今回は Ruby 2.5 のおさらいということで Ruby 2.5 の機能を参加者全員で眺めて生きながら意見を言っていくような内容でした。
当日読んでいた Ruby 2.5 に関する資料
以下、気になった機能を読んでいるときに出た意見などの適当なまとめ。
ちなみにわたしはまだ Ruby 2.5 の機能は殆ど触れてないです。
バックトレースおよびエラーメッセージが逆順に出力されるようになった
以下のようにバックトレースの順番が代わりました。
Ruby 2.4
$ ruby2.4 main.rb main.rb:3:in `map': undefined method `to' for 1:Integer (NoMethodError) from main.rb:3:in `homu' from main.rb:8:in `func' from main.rb:11:in `<main>'
Ruby 2.5
$ ruby main.rb Traceback (most recent call last): 3: from main.rb:11:in `<main>' 2: from main.rb:8:in `func' 1: from main.rb:3:in `homu' main.rb:3:in `map': undefined method `to' for 1:Integer (NoMethodError)
- 慣れの問題
- リリースされたらみんな使うのでいろいろと意見が出てきそう
- わたしはまだ確認してないのでなんとも言えない
トップレベルの定数検索が Object から削除された
以下の用にトップレベルで定義したクラスなどが X::Toplevel
のように ::
で参照できなくなった。
class Toplevel end class X end # この時にトップレベルの定数(クラスなど)が参照できなくなった X::Toplevel
- 今までの仕様は挙動としては正しいけど意図しているかは微妙だった
- Ruby の挙動として見ると Object が検索されるのはわかる
- Object 内で明示的に定義したクラスも参照されない
class Object class X end end class X2 end # エラー X2::X
yield_self
#tap
と似ているがブロック内の戻り値を返す。
# yield_self だと break は不要 p [1, 2, 3].yield_self { |myself| myself.map { |it| it + myself.size } } # => [4, 5, 6]
- 便利そう
#tap
+break
を使っていたコードはyield_self
で置き換えられる- 名前が長い
- Ruby 2.5 で追加される yield_self メソッド - Secret Garden(Instrumental)
Procの確保を遅延
- 今まではブロック引数を明示化しているよりも『していないほうが』パフォーマンスがよかった
- ブロック引数を明示化したほうがわかりやすいのでなるべくパフォーマンスに影響がでないように対応した
- jruby だと逆にブロック引数を明示化したほうが早かった
- パフォーマンスの問題はなくなったのでみんなブロック引数を明示化しよう
- Ruby 2.5 は引数に &block を書いても速い!!! - onk.ninja
式展開の #to_s が refinements に対応
class X end using Module.new { refine X do def to_s "class X" end end } # refinements で定義された #to_s が呼ばれる p "output: #{X.new}" # => "output: class X"
- 便利
- ほかの内部で呼ばれるメソッドも refinements に対応してほしい
respond_to?
やto_proc
も refinements に対応してほしい
Integer.sqrt
p Integer.sqrt(9) #=> 3 p Integer.sqrt(15) #=> 3 p Integer.sqrt(16) #=> 4
- なぜ
Integer
のクラスメソッドに…? - ユースケースがわからん
String#casecmp で例外が発生しなくなった
Ruby 2.4
# Error no implicit conversion of Symbol into String (TypeError) "homu".casecmp(:homu) # Error no implicit conversion of Symbol into String (TypeError) "homu".casecmp(1234)
Ruby 2.5
p "homu".casecmp(:homu) # => nil p "homu".casecmp(1234) # => nil
TypeError
のままでもよかったのでは?Symbol
だとnil
を返していた- 一貫性のために
TypeError
から nil を返すように変更- と思ったけど Symbol でも
TypeError
だったのでうーん?
- と思ったけど Symbol でも
- やっぱり
TypeError
のままの方がよかったのでは?
ERB#result_with_hash
が追加
require "erb" p ERB.new("Hello, <%=name%>").result_with_hash(name: "homu") # => Hello, homu
#result_with_hash
でコンテキストを渡した場合でも外部のコンテキストは参照できる- それならあまり意味が無いような気がする
- コンテキストを明示的に上書きすることはできる
Set#===
case :apple # Set のいずれかに含まれていれば when Set[:potato, :carrot] then 'vegetable' when Set[:apple, :banana] then 'fruit' end #=> "fruit"
- 集合と集合で比較するのではなくて
#include?
を使う Set
オブジェクト同士を比較したい?#include?
を呼び出すのは微妙かと思ったけどまあSet
ならいいかーMethod#===
を使用してs.method(:include?)
みたいに代用することも可能
個人的に #===
を #include?
にするのはなんかなーと思ってましたがまあ Set だからいいかーという位置に落ち着きました。
Range も同じですしね。
ただし、Array#=== を #include? のエイリアスにするのは絶対に許さん。
Ruby 2.5 に対する所感
そんな感じでざっくりまとめてみました。
上記以外にも Ruby 2.5 ではいろいろとメソッドや機能なんかが追加されたんですが割と『それはそうだよねー』みたいな機能とかが多かった印象です。
個人的によさげなのはやっぱりブロック引数周りのパフォーマンスの向上ですかね。
そもそもブロック引数を明示化してないのにブロック引数を渡すことができるという挙動があまり好きではないので、この機会に『ブロック引数を受け取る場合は必ずブロック引数を明示化する』ようになってほしいです。
あと Method#===
もそうですが Set#===
が入っていたのが知らなかったのでちょっと驚きました。
#===
ブーム来てるぞ…。
他には全員に影響があるって意味でバックトレース順が変わることですかねえ。
慣れの問題だったり、Ruby 2.5 の仕様のほうが見やすいって人もいると思いますが、やはり今までと表示形式が変わることに対する影響はコードの仕様が変わるぐらい大きいと思います。
この部分は Ruby 2.5 がリリースされてから色々と意見が出てきそうですねえ。
あ、あとそろそろ refinements さんはどこまでの範囲が影響するのかをしっかりとまとめてほしいところ。
参加してみて
そんな感じで初参加してきましたー。
参加する前はもくもく会みたいな感じで各々が Ruby 2.5 について調べるのかと思っていたのですが、実際は全員で機能を眺めて行くような感じでした。
会自体そこまで重い雰囲気ではなかったので割りと好き勝手意見を言うことができたり、他の人の意見なんかも聞くことができたのでとてもよかったです。
基本的にぼっちなのでこういうみんなで Ruby に対する意見を言い合うような機会は殆ど無いのでとても新鮮でした。
思っていることは割りとみんな同じなんだなーという風に共感できたのもよかったですね。
あと参加者の殆どは Ruby ユーザ(Ruby 本体の開発者ではない)なのでそういう目線で自由に意見を言っているのもいいなーと思いました。
結果的に Ruby 2.5 で追加される機能がなんとなく理解できたので参加してよかったです。
運営の方がありがとうございました。
【一人 vimrc advent calendar 2017】autocmd FileType 時に設定を追加する場合の注意点【20日目】
一人 vimrc advent calendar 2017 20日目の記事になります。
前回filetype
の設定に関する記事を書いたのですが、今回はそれに関係するお話。
autocmd FileType
等で設定するときの注意点
autocmd FileType
などで『任意の filetype
に対して』設定を行う際は基本的には『そのバッファでのみ』反映される設定を行います。
ですので、オプションやキーマップなどはすべてバッファローカルにして設定する必要があります。
オプションの設定
オプションを設定する場合は :set
ではなくて :setlocal
を使用します。
" なんかいろいろ設定 setlocal cindent setlocal cinoptions+=:0 setlocal matchpairs+=<:>
ただし、オプションによってはバッファローカルではなくてグローバルでしか設定できないオプションがあるので注意してください。
キーマッピング
キーマッピングは引数に <buffer>
を追加することでバッファローカルとして設定することができます。
" 最後に定義された include 箇所へ移動してを挿入モードへ nnoremap <buffer> <Space>ii :execute "?".&include<CR> :noh<CR> o
Ex コマンド
Ex コマンドの場合は -buffer
を追加します。
command! -buffer Hoge echo "hoge"
autocmd
autocmd
の場合はちょっと手間で、augroup
を利用します。
" filetype 用の augroup を用意する augroup my-vimfiler " その augroup の <buffer> を削除する autocmd! * <buffer> " autocmd は <buffer> を追加して定義する " こうすることでバッファローカルの autocmd になる autocmd CursorHold <buffer> execute "normal \<Plug>(vimfiler_print_filename)" " autocmd CursorMoved <buffer> execute "normal \<Plug>(vimfiler_print_filename)" augroup END
このようにグローバルの autocmd
と同様に autocmd
が2回設定されることを防ぐために augroup
を利用する必要があります。
まとめ
- オプションなどを設定する場合は何に対して設定するのか注意しよう
ちなみに1つのバッファで複数の filetype
を切り替えて使う場合の注意点もあるんですが、気力が尽きたので機会があれば別に書きます…。
【一人 Ruby Advent Calendar 2017】Shinjuku.rb #56 2017年開発厄落としLT大会でLTしてきた【20日目】
一人 Ruby Advent Calendar 2017 20日目の記事になります。
Shinjuku.rb で LT してきました。
今回も遅れてしまい申し訳なく。
case-when と === と gem
https://osyo-manga.github.io/slide-shinjukurb-56-case-when/#/
学生LTでも同じような事を話したのですが今回はもうちょっとマニアックな話をしてきました。
まあ何が言いたかっていうと Method#===
が入ったって言いたかっただけですね…。
と、いうかお酒を飲んだ勢いでライブコーディングしましたがぐだってしまって申し訳ねえ…。
ちなみに『なぜそんなに case-when
?』みたいな質問があったんですが、元々別の用途で ===
を使いたくていろいろと調べていたら『あれ、case-when
もよくね?』と思った所存です。
実際、JavaScript のように厳密演算子みたいな用途ではなくて case-when
専用として #===
が定義されているので拡張性はとても高いと思います。
Ruby 2.5 で Set#===
も追加されたのでどんどん #===
使っていきたい。
所感
今回はほぼ忘年会みたいな感じでお酒を呑みながらビザを食べて人をダメにするソファーに座りながら LT を聞くという素晴らしい会でした。
他の地域.rb と同様にとても話しやすい雰囲気でしたね。
お酒飲んでるとみんないい感じにいいたい放題言っていたのがとてもよかったです。
表題は今年の振り返りということでしたがわたしも割りと好き勝手話してきました。
また機会があれば、参加してみたいと思います。
運営の方々ありがとうございましたー。
【一人 vimrc advent calendar 2017】Vim から git commit する【19日目】
一人 vimrc advent calendar 2017 19日目の記事になります。
そろそろネタが尽きてきてそう。
Vim から git commit する
さて、Vim を使っているとその場でファイルを編集して、そのままファイルをコミットしたいと思うことが多いと思います。
そういう時に以下のようなコマンドを用意しておくと Vim からサクッと git commit
する事ができるので便利です。
" :GitCommit {message} command! -nargs=* GitCommit \ echo system(printf("git commit %s -m %s", expand("%:p"), shellescape(<q-args>)))
こんな感じでよく使う外部コマンドも Vim のコマンドとしておくと便利です。
同様にコミットの取り消しもコマンド化しておくと便利です。
command! -nargs=* GitReset echo system("git reset --soft HEAD^")
便利。
こんな感じによく使うような外部コマンドや覚えるのがめんどくさい外部コマンドを Vim のコマンドとして定義しておくととても捗ります。
【一人 Ruby Advent Calendar 2017】トップレベルの変数の扱い【19日目】
一人 Ruby Advent Calendar 2017 19日目の記事になります。
前回、トップレベルのメソッドの扱いについて書いたのですが、今回は変数に関してです。
トップレベルのローカル変数
トップレベルで定義されたローカル変数は通常のローカル変数と同じような感じで使えます。
homu = "homu" def func homu end # 関数内からは参照できない # Error: undefined local variable or method `homu' for main:Object (NameError) # p func # 関数内で参照する場合はブロックを使ってキャプチャする define_method(:func){ homu } p func # => "homu"
また、ローカル変数の寿命は『プログラム終了』までで、他のファイルからは参照できません。
トップレベルのインスタンス変数
さて、ややこしいのはトップレベルで定義されているインスタンス変数です。
トップレベルで定義されたインスタンス変数は self
、つまり main
オブジェクトのインスタンス変数として定義されます。
@homu = "homu" p self.instance_variables # => [:@homu] p self.instance_variable_get :homu # => "homu"
また、トップレベルのメソッド内等からも参照できるように『見えます』。
@homu = "homu" def homu @homu end p homu # => "homu"
これがちょっと勘違いするポイントなんですが、
わけではなくて
- トップレベルで呼び出したメソッドのコンテキストが
main
オブジェクトになる
のでメソッド内で参照しているインスタンス変数が self
、つまり main
オブジェクトから参照する事になります。
まあ文章で説明してもわかりづらいと思うので実際にコードを書いてみましょう。
def homu self end # トップレベルで呼び出した場合、homu メソッドのコンテキストは main になる p homu # => main class X def func homu end end # 別のオブジェクトのメソッド内で呼び出した場合は、そのオブジェクトのコンテキストになる p X.new.func # => #<X:0x00000000008796a0>
このようにトップレベルで定義されたメソッドは『呼びだされたレシーバのコンテキスト』として呼ばれます。
なので、トップレベルのメソッド内でトップレベルのインスタンス変数を参照しようとすると呼び出された場所によって結果が異なります。
@homu = "homu" def homu @homu end # トップレベルで呼びだされた場合、コンテキストは main になるので # main のインスタンス変数、つまりトップレベルの @homu が参照される p homu # => "homu" class X def func homu end end # X のインスタンスメソッドから呼び出した場合は、コンテキストが X オブジェクトになるので # X オブジェクトの @homu を呼びだそうとする # X オブジェクトのインスタンス変数 @homu は未定義なので nil を返す p X.new.func # => nil
うーん、ややこしいですね…。
まとめ
- トップレベルで定義したローカル変数はそのファイルのローカル変数として定義される
- トップレベルで定義したインスタンス変数は
main
オブジェクトのインスタンス変数として定義される - トップレベルで定義されたメソッドのコンテキストはレシーバによって異なるので注意する
この挙動は Ruby の
self
がどのように参照されているのかObject
で定義されたメソッドはどのように扱われるのか
などを理解していればとても理にかなっている挙動にはなるんですが、そのあたりがわかっていないとかなり難しいところではありますね…。
【一人 Ruby Advent Calendar 2017】トップレベルメソッドの扱い【18日目】
一人 Ruby Advent Calendar 2017 18日目の記事になります。
Ruby でトップレベルに定義したメソッドがどういう扱いなのかイマイチわからなかったので調べてみた。
トップレベルの self
まず、トップレベルの self
ですが、これは main
という特別なオブジェクトになります。
p self # => main
また、main
という名前では参照することは出来ません。
トップレベルで定義したメソッドはこの main
オプジェクトの private
メソッドとして定義されます。
def homu "homu" end p self.private_methods(false) # => [:include, :using, :public, :private, :define_method, :DelegateClass, :homu] # Error: private method `homu' called for main:Object (NoMethodError) # p self.homu p self.send :homu # => "homu"
main で定義されたメソッドは Object
のインスタンスメソッドとしても定義される
ここが重要ぽいんですが、先ほどのようにトップレベルで定義したメソッドは『Object
の private
なインスタンスメソッド』としても定義されます。
def homu "homu" end p Object.private_instance_methods(false) # => [:DelegateClass, :homu]
Object
のインスタンスメソッドとして定義されているので『Object
を継承しているクラス(つまり殆どのオブジェクト)』からトップレベルのメソッドを呼び出すことが出来ます。
def homu "homu" end class X # Object#homu なのでどこからでも呼べる p homu #x=> "homu" def mado homu + homu end end p X.new.mado # => "homuhomu" # ただし、private メソッドなのでレシーバを付けた呼び出しは出来ない # Error: private method `homu' called for #<X:0x0000000000ae12d0> (NoMethodError) # p X.new.homu
ちなみに以下のように BasicObject
を継承している場合は Object
は継承していないので使用することは出来ません。
def homu "homu" end class X < BasicObject def mado # Error: undefined local variable or method `homu' for #<X:0x00000000021fd720> (NameError) homu + homu end end p X.new.mado
トップレベルで def homu
と def self.homu
した場合の違い
余談ですが、トップレベルでメソッドを定義した場合と self
に対して特異メソッドを定義した場合の追加です。
def self.singleton_method_added name return if name == :singleton_method_added p name end # self.singleton_method_added は呼ばれない def homu end # private メソッドとして追加される p Object.private_instance_methods(false) p self.private_methods(false) # self.singleton_method_added は呼ばれる def self.mami end # Object には追加されない p Object.instance_methods(false) # self の public メソッドに定義される p self.methods(false)
トップレベルでメソッドを定義した場合と self
の特異メソッドとして定義した場合では挙動がかなり違います。
そもそもトップレベルのメソッドは main
ではなくて Object
に定義されるのでは…?
トップレベルで定義したメソッドは『main
オプジェクトの private
メソッド』として定義されると思っていたんですが、そもそも『main
に定義される』のではなくて『Object
に定義される』という方が正しいのではないでしょうか。
これにより『Object
にメソッドが定義されること』で結果的に『main
でも使用することが出来る』という挙動の方が個人的には自然な気がします。
このあたりは Ruby のソースコードを読んでみないとわかりませんが、認識としてはこっちの方がわかりやすい…。
まとめ
【一人 vimrc advent calendar 2017】現在の filetype に対して簡単に設定を追記する【18日目】
一人 vimrc advent calendar 2017 18日目の記事になります。
filetype
に対する設定
さて、vimrc
で任意の filetype
に対して設定を行う場合、以下のように autocmd
を使用することが多いです。
" filetype=cpp に対する設定 autocmd Filetype cpp setlocal matchpairs+=<:>
上記のような設定でもいいんのですが vimrc
の量が増えてくると管理しづらくなったり、新しく設定を追記するのもちょっと手間になってきます。
そこで、次のようなスクリプトを用いて vimrc
ではなくて『Vim プラグインとして』扱うと管理しやすくなります。
Vim プラグインとして filetype
用のファイルを管理する
まず、保存するディレクトリを決めます。
" このパスに設定を保存するようにする let $VIMFILETYPE_PLUGIN = "~/.vim/filetype"
次にこのディレクトリが Vim から読み込まれるように 'runtimepath'
へと追記します。
set runtimepath+=$VIMFILETYPE_PLUGIN
このように 'runtimepath'
に追加することで filetype=hoge
を行った際に自動的に #VIMFILETYPE_PLUGIN/ftplugin/hoge.vim
などファイルが読み込まれるようになります。
最後に以下のような編集用のスクリプト用いて filetype
に対する設定ファイルを簡単に開きます。
command! EditFileType execute "split " . $VIMFILETYPE_PLUGIN . "/after/ftplugin/" .&filetype. ".vim" nnoremap <silent> <Space><CR> :<C-u>EditFileType
あとは :EditFileType
などでスクリプトファイルを開いてそこに設定を記述していけば、:set filetype=
を行った際にその設定が読み込まれるようになります。
キーマッピング(<Space><CR>
)や開き方(split
)などはお好みで変えてください。
また、注意点としては、
- 設定を記述しただけでは反映されない
- 再度
:set filetype={filetype}
を行うことで反映される
- 再度
- グローバル(
:set
)ではなくてローカル(:setlocal
)で設定を記述する
あたりですかね。
ちなみにランタイムのファイルよりも後に読み込まれるように $VIMFILETYPE_PLUGIN/ftplugin/
ではなくて $VIMFILETYPE_PLUGIN/after/ftplugin/
にファイルを保存しています。
こうすることで既存の設定を上書きすることが出来ます。
まとめ
filetype
ごとの設定はautocmd FileType
を使うのが簡単vimrc
で管理しづらくなってきたらプラグインとして管理するのも手
わたしは割と初期の頃からこの設定を使っているんですがかなり便利です。