ã¾ãã©ã馴æã¿ãä½ã£ã¦ã¿ãã·ãªã¼ãºã§ãã
ä»åã¯ãRaaP(ã©ã¼ã)ã¨ãããã¼ã«ãä½ãã¾ãããRBS as a Propertyã§RaaPã§ãã
RaaPã¯ãã¹ãã£ã³ã°ãã¼ã«ã®ä¸ç¨®ã§ãRBSããã®ã¾ã¾ãã¹ãã³ã¼ãã«ã¿ãã¦ã¦å®è¡ãã¦ããããã¼ã«ã§ãã
次ã®ãããªRBSããã£ãã¨ãã¦
class Foo end class Bar def initialize: (foo: Foo) -> void def f2s: (Float) -> String end
ã¤ãã®ãããªãã¹ãã³ã¼ããèªåçã«ä½ã£ã¦å®è¡ãã¦ãããã¤ã¡ã¼ã¸ã§ãã
describe Bar do let(:foo) { Foo.new } let(:bar) { Bar.new(foo: foo) } it "#f2s" do 100.times do |size| float = Random.rand * size expect(bar.f2s(float)).to be_a(String) end end end
ããããã£ãã¼ã¹ãã¹ãã«ãããããããã£ã¨ã¯ãã©ããªã©ã³ãã ãªå¤ãæ¥ã¦ãå ±éã§æåãããã¹ãã±ã¼ã¹ã®ãã¨ã§ãã
RBSãããããã£ã¨ãã¦èããã"RBS as a Property"ã¨ããããã§ãã
å®éã¯è¿½å ã®ãã¹ãã³ã¼ãã®è¨è¿°ã¯ä¸åå¿ è¦ããã¾ãããRBSãæ¸ãã ãã§ãã¹ãã³ã¼ãã¨ãã¦ã使ããããæ¸ããRBSã¯ãã¡ããSteepçã§å©ç¨ã§ãã¾ãããããåºæ¬çãªã³ã³ã»ããã§ãã
çæ³
æåã®çæ³ã¯ãgolangããã§ããã
ç§ã¯rgotã¨ãã誰ã使ã£ã¦ããªããã¹ãã£ã³ã°ãã¼ã«ãæç¨ãã¦ããã®ã§ãããããã¯golangã®æ¨æºãã¹ãã£ã³ã°ã©ã¤ãã©ãªã¼ã§ããtesting PackageãRubyã§ãã®ã¾ã¾æ¸ãã¦ã¿ããã®ã§ããRubyKaigiã®LTã§ãçºè¡¨ãããã¨ãããã®ã§ããã®ã¨ãã«ãã¹ãã®èãæ¹ã«ã¤ãã¦ã¯èªåãªãã®ã¢ãã«ãä½ããã¨æãã¾ãã
ä¹
ãã¶ãã«rgotã®ã¡ã³ãããããã¨golangã®testing Packageãã®ããã¦ã¿ãã¨ãrgotå®è£
æã«ã¯ãªãã£ãFuzzing
ã¨ããæ©è½ã追å ããã¦ãã¾ããããªãã®æ©è½ããããããã¨ãããããã¼ããã¦ã¿ãã¨ããªãã»ã©ãåãæå®ããã¨ã©ã³ãã ãªå¤ãçæãã¦ãã¹ããå®è¡ããæ©è½ã ã£ãããã§ãããããèãæ¹ãããã®ãã¨æå¿ãã¾ããã
å®è·µããããã£ãã¼ã¹ãã¹ã
ããããããã«ãå®è·µããããã£ãã¼ã¹ãã¹ãããèªãã ãã¨ã§æ°ãããã¹ããã©ãã¤ã ããããã¨ãé常ã«é¢ç½ãæãã¾ããã
『実践プロパティベーステスト ― PropErとErlang/Elixirではじめよう』www.lambdanote.com
ãã®æ¬ãèªãã§ããæã«ããããããªèããæµ®ãã³ã¾ãã
ããããRubyã§è¡¨ç¾ãããã©ããªæãããªãã
ãQuickCheckã¯åãéè¦ãªHaskellã§ãã¾ãã¦ããRBSã使ã£ã¦ä½ãã§ããªãããªãã
ããã®integer()
ã£ã¦ããã¸ã§ãã¬ã¼ã¿ã¼ãRBSããä½ããªãããªãã
ãRBSãæ¸ããããã¹ãã³ã¼ããèªåçæããã®ã¯ã©ãã ããï¼ã
ãã£ã¦ããããã®ã¾ã¾å®è¡ãããããããããªãï¼ã
ãããããã°ãã¹ãã³ã¼ãã®äºææ§ã¨ãã§æ©ã¾ãªãã¦ãæ¸ãããã³ã¼ããæ¸ããªãã¦ããããªã使ããæ軽ããããããã³ã¼ãã®ãã¹ããRBSã®ãã¹ããã§ããããææªãã¼ã«ã使ããªããªã£ã¦ãRBSã¯æ®ãããããªãï¼ã
ããããã°ããªãï¼ã
ã¨ãããªæãã§éçºãã¦ã¿ããã®ã«ãªãã¾ãã
å®ç¸¾ããã¿ã使ç¨ä¾
ãã®ãã¼ã«ã¯éçºä¸ã§ãå¤ãã®ææãåºãã¦ãã¾ãã
Symbol#=~
ç°¡åãªä¾ããç´¹ä»ãã¾ãããã
https://github.com/ruby/rbs/pull/1704
ããã§ã¯Symbol
ã®def =~: (untyped obj) -> Integer?
ãä¾ã«è¦ã¦ã¿ã¾ãã
untyped
ã¯ããããanyåã§ãå
¨ã¦ã®åããããããã¨ã表ãã¾ãã
ããããªãããå®éã¯ãªãã§ãããããã§ã¯ããã¾ããã
:sym =~ 1 # => undefined method `=~' for an instance of Integer (NoMethodError)
Integerãå¼æ°ã«æ¸¡ãã¦ã絶対ã«æåãã¾ããããã®ã¾ã¾ã§ã¯Steepã§ãã§ãã¯ããã¨Integerã渡ãã¦ãåã¨ã©ã¼ã«ãªãã¾ããããã®RBSã¯ä¸ååã§ãããã¨ããããã¾ãã
ããã¾ã§ruby/rbsãªãã¸ããªã¼ã§ã¯ãRBSã®è¨è¿°ã®ç¢ºããã¯ã©ã®ããã«ç¢ºèªããã¦ããã®ã§ããããï¼
ãã¡ããããã¹ããå ¨ãç¡ãã£ãããã§ã¯ããã¾ããã
Symbol#=~
ã®ããã«ã¯ã以ä¸ã®ãã¹ããç¨æããã¦ãã¾ããã
assert_send_type "(Regexp) -> Integer", :a, :=~, /a/ assert_send_type "(nil) -> nil", :a, :=~, nil
ããããRBSæçæã大éã«ããcoreã¡ã½ããä¸ã¤ä¸ã¤ç¢ºèªãã¦ãããã¨ã¯é£ãããã¡ã½ããåã ãã§ãã¨åãç¨æãããã®ã§ãããããããããæ£ç¢ºãªRBSãèããã®ã¯äººéã«ã¯å¤§å¤ãªä½æ¥ãªã®ã§ãã
ããããªããããã§ã¯ãRBSã®è¡¨ç¾ã«å¯¾ãã¦ãã¹ããã¿ã¼ã³ã足ããªãã£ãããã§ãã
RaaPã§è©¦ãã¦ã¿ã¾ãããã
$ bundle exec raap 'Symbol#=~' # Symbol ## def =~: (untyped obj) -> ::Integer? SE, [2024-03-22T22:45:57.257849 #92819] ERROR -- : [TypeError] type mismatch: String given F Failed in case of `:v.=~(:r) -> nil[NilClass]` ### call stack: ``` :v.=~(:r) ``` success: 0, skip: 1, exception: 0 Fail: def =~: (untyped obj) -> ::Integer?
:v.=~(:r)
ã¨ããã±ã¼ã¹ã§ãTypeError
ãèµ·ããã¨å ±åãã¦ãã¾ãã
untyped
ã¨ãã表ç¾ãããæ§ã
ãªãªãã¸ã§ã¯ãã§å¼ã³åºãã試ã¿ãTypeError
ãèµ·ãããã¿ã¼ã³ããåç¾ã³ã¼ãä»ãã§æãã¦ããã¾ããã
æ©æ¢°çã«ãã¹ãã±ã¼ã¹ãçæãã¦è©¦ããã¨ã§ã人éã«ã¯å¤§å¤ã ã£ããã¹ãã±ã¼ã¹ã®çæã大éã«è¡ããã¨ãã§ããRBSã®ä¸ååãªç¹ãCLIã³ãã³ãä¸çºã§è¦ã¤ããã¾ããã
Integer#pow
ããå°ã大ããä¾ãç´¹ä»ãã¾ãã
https://github.com/ruby/rbs/pull/1706
ããã¯ä»¥åã®éçºæ¥è¨ã§ãç´¹ä»ããããã«ãåã®å°é家ã§ãã£ã¦ãæ°ä»ããªãé£ããåã§ãã
RaaPãªãç°¡åã«åç¾ã³ã¼ãä»ãã§åã®ééããæãã¦ããã¾ãã
$ bundle exec raap 'Integer#pow' # Integer ## def pow: (::Integer other, ?::Integer modulo) -> ::Integer EI, [2024-03-22T22:53:31.836090 #93160] INFO -- : Exception: [ZeroDivisionError] divided by 0 ..EI, [2024-03-22T22:53:31.836288 #93160] INFO -- : Exception: [ZeroDivisionError] divided by 0 ..F Failed in case of `2.pow(-1) -> (1/2)[Rational]` ### call stack: ``` 2.pow(-1) ``` success: 4, skip: 0, exception: 2 ## def pow: (::Float) -> ::Float ...F Failed in case of `-2.pow(-4.25) -> (0.037162722343835025-0.037162722343835025i)[Complex]` ### call stack: ``` -2.pow(-4.25) ``` success: 3, skip: 0, exception: 0 ## def pow: (::Rational) -> ::Rational ..F Failed in case of `3.pow((3/11)) -> 1.3493480275940617[Float]` ### call stack: ``` rational = Rational(-3, -11) 3.pow(rational) ``` success: 2, skip: 0, exception: 0 ## def pow: (::Complex) -> ::Complex .................................................................................................... success: 100, skip: 0, exception: 0 Fail: def pow: (::Integer other, ?::Integer modulo) -> ::Integer Fail: def pow: (::Float) -> ::Float Fail: def pow: (::Rational) -> ::Rational
3ã¤ã®ã±ã¼ã¹ã§ééãããããã¨ããåç¾ã³ã¼ãä»ãã§æãã¦ããã¾ããã
Rational
ã使ç¨ããã±ã¼ã¹ã§ã¯call stack
ã¨ããåç¾ã³ã¼ãã2è¡ã«åããã¦ãã¾ãã
ããããã®ã¾ã¾å®è¡ããã°ãåãããããã£ããã¿ã¼ã³ãåç¾ã§ããããã§ãã
String#initialize
https://bugs.ruby-lang.org/issues/20292
éçºéä¸ã§Rubyã®ãã°ãè¦ã¤ãå ±åãã¦ãã¾ãããããªã³ã¼ã人éã¯ã¾ãæ¸ããªãã¨æãã¾ãããã©ã³ãã ãªå¤ã«ãããã¹ããªãã¿ã¤ãåºããã®ã§å¯è½æ§ãæãã¾ããç¾å¨ã®RaaPã§ã¯ãpublicã¡ã½ããã®ã¿ç¢ºèªããããã«å¤æ´ããã®ã§åç¾ã³ã¼ãã¯ä½ãåºãã¾ãããprivateã¡ã½ããã¯ãªãã·ã§ãã«ã§å®è¡ã§ãã¦ãããã®ããªâ¦â¦ï¼
ã©ããã£ã¦è¦ã¤ããã®ï¼
RaaPã§ã¯ã«ã¼ãã¨ãªãmodule/classåãæå®ããã°ããã®ååã§å®ç¾©ããã¦ããã¡ã½ããå ¨ã¦ã§ãã¹ãã§ããããã«ãªã£ã¦ãã¾ãã
åºåãå°ããããªæ¢ç¥ã®classã§è©¦ãã¦ã¿ã¾ãã
$ bundle exec raap 'TrueClass' # ::TrueClass#! ## def !: () -> false .................................................................................................... success: 100, skip: 0, exception: 0 # ::TrueClass#=== ## def ===: (true) -> true .................................................................................................... success: 100, skip: 0, exception: 0 ## def ===: (untyped obj) -> bool .................................................................................................... success: 100, skip: 0, exception: 0 # ::TrueClass#inspect ## def inspect: () -> "true" .................................................................................................... success: 100, skip: 0, exception: 0 # ::TrueClass#to_s ## def to_s: () -> "true" .................................................................................................... success: 100, skip: 0, exception: 0 # ::TrueClass#to_json ## def to_json: (?::JSON::State state) -> ::String .................................................................................................... success: 100, skip: 0, exception: 0 # ::TrueClass#& ## def &: (false | nil) -> false .................................................................................................... success: 100, skip: 0, exception: 0 ## def &: (untyped obj) -> bool .................................................................................................... success: 100, skip: 0, exception: 0 # ::TrueClass#^ ## def ^: (false | nil) -> true .................................................................................................... success: 100, skip: 0, exception: 0 ## def ^: (untyped obj) -> bool .................................................................................................... success: 100, skip: 0, exception: 0 # ::TrueClass#| ## def |: (untyped obj) -> true .................................................................................................... success: 100, skip: 0, exception: 0
è¤æ°ã®ã¡ã½ããã横æçã«ç¢ºèªãããã¨ãã§ãã¾ããã
ä»ã«ãããã¤ãruby/rbsã®RBSã§ã®ééããã¿ã¤ãã¦ãã¾ãã
ãããã
ãã®ãã¼ã«ã«ã¯å¯è½æ§ãæãã¦ããã®ã§ããããªãæç¨æ§ãæ¢ç©¶ãã¦ããããã¨æãã¾ãã
RBSããã®ã©ã³ãã çæã ã¨ãã©ããã¦ãã«ã¹ã¿ã ãªã³ã¼ãããªãã¨ç¡çãªã±ã¼ã¹ãå¤ãåå¨ãã¾ãã ãã®ãããããã¾ã§ç¬¬ä¸åªå ã¯CLIããã®å©ç¨ã¨èãã¤ã¤ããæ¢åã®ãã¹ãã³ã¼ãã«çµã¿è¾¼ãã表ç¾ãèãã¦ãã¾ãã
ããã¾ã§ã¯CRubyã®coreãªclassã«å¯¾ãã¦å®é¨ãã¦ããã®ã§ãå®è£ ã¯ããªãä¿¡é ¼ã«ãããã±ã¼ã¹ãå¤ãã£ãã®ã§ããã å®éã®ã¢ããªã±ã¼ã·ã§ã³ã§ã©ã®ãããªæç¨æ§ãçºæ®ã§ããã確èªã§ãã¦ããªãã®ã§ããã®è¾ºãæ¢ã£ã¦ããããã¨æãã¾ãã
ç¡çç¢çããã¨ãããæ¢ãããããªãã¦èª²é¡ãã¼ã¹ã§èãããã£ã¦ï¼
ã¯ã¯ã