Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Paweł Świątkowski
Paweł Świątkowski

Posted on

     

Ruby – Pattern Matching – Second Impressions

Since I publishedA quest for pattern-matching in Ruby 3 years ago, I've been called "pattern matching guy" more than once. So obviously, when I learned that PM is inevitably coming to the Ruby core, I was curious to check it out.First Impressions have already been published, so this is "Second Impressions", from my point of view.

Heads up: it's very subjective.

I'm mostly judging it by examples provided in theoriginal Redmine ticket, such as:

classArrayaliasdeconstructitselfendcase[1,2,3,d:4,e:5,f:6]ina,*b,c,d:,e:Integer|Float=>i,**fpa#=> 1pb#=> [2]pc#=> 3pd#=> 4pi#=> 5pf#=> {f: 6}e#=> NameErrorend

First of all, the code is ugly in a way that makes it hard to reason about. It looks like being added on top of a language which was not designed to support pattern matching (which is exactly the case). This might not be important in the long run, when people get used to it - but here it is, in the second impressions round.

Destructuring (why was it called deconstruction?) looks nice, but I would remove the pipe thingy. Instead ofe: Integer | Float => i (which is terribly ambiguous - is ite: (Integer | Float) => i or((e: Integer) | (Float)) => i, or something else?) it would be better to have a possibility to define a type unionlike in Pony. For example:

number=typeunion(Integer|Float)# hypothetic keyword typeunioncaseninnumberputs"I'm a number"inStringputs"I'm string"end

Besides that it's good, especially for getting things out of hashes.

But probably my most important problem with this proposal is that it does not let me have multiple functions defined with different type signatures, ruled by pattern matching. This is what I'm mostly missing on a daily basis working with Ruby, while having it available in Erlang or Elixir. To give you a taste of what I'm talking about:

classWriterdefwrite_five_times(text=>String)putstext*5enddefwrite_five_times(text=>Integer)putstext.to_s*5enddefwrite_five_times(text=>Object)raiseNotImplementedErrorendend

Of course, to achieve what's in code listing above, it would be much larger and complicated change. Basically it would be like introducing proper types to Ruby. It needs to allow having one method defined mutiple times in one class, but without shadowing previous definitions. I don't think that Ruby will ever go this way, yet this is something that would clean up my code in few places significantly.

I also realised that while working onNoaidi – my implementation of pattern matching. I don't really want plain pattern matching somewhere in the code, as I can make most cases work with good oldcase in Ruby. But I would like to be able to write modules that behave kind of like the ones in Elixir.

And this is being made possible in Noaidi. I have an experimental branch enabling this and I hope I will be able to finish it some day. Such module would look like this:

moduleMathextendNoaidi::DSLfun(:fib,0..1){1}fun(:fib,Integer){|n|add(fib(n-1),fib(n-2))}fun(:fib,Object){raiseNotImplementedError}funp(:add,Integer,Integer){|a,b|a+b}endMath.fib(1)#=> 1Math.fib(20)#=> 10946Math.fib("test")#=> NotImplementedErrorMath.add(1,3)#=> NoMethodError (private method `add' called for Math:Module)

Verdinct: I'm kind of disappointed. The quest is not over yet.


This has been also posted onmy personal blog.

Top comments(6)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
edisonywh profile image
Edison Yap
An aspiring software engineer from the tiny city of Kuala Lumpur.
  • Location
    Stockholm, Sweden
  • Education
    RMIT University, Melbourne
  • Work
    Software Engineer at Klarna
  • Joined

Wow, that was really hard to understand!

I'm sure whoever made this happen had put in a lot of effort, but as you said, I don't think it's ideal to just shoehorn a feature in when it doesn't fit in.

By the way, with a quick glance on your snippets below, it looks like what you're asking for isn't reallyjustpattern matching, but rathermethod overload.

I knowCrystal has it which looks pretty cool!

CollapseExpand
 
katafrakt profile image
Paweł Świątkowski
  • Location
    Wrocław, Poland
  • Pronouns
    he/him
  • Work
    Elixir and ReScript developer @ Walnut
  • Joined

From what I know about Crystal, it has method overload only by types (much like Java). And hear it would be possible to also "overload" by particular values - for example different method for hash containing a key "success".

CollapseExpand
 
edisonywh profile image
Edison Yap
An aspiring software engineer from the tiny city of Kuala Lumpur.
  • Location
    Stockholm, Sweden
  • Education
    RMIT University, Melbourne
  • Work
    Software Engineer at Klarna
  • Joined

Correct, that's what overloading means - defining methods with same name but with different methodsignatures, and method signatures are usually types & arity.

What you're referring to (in Elixir) is a combination of both - when you have pattern matching, your method signatures aren't just types & arity, but also patterns.

CollapseExpand
 
vidarh profile image
Vidar Hokstad
  • Joined

No need for language support for your "typeunion" suggestion (though really, it's more generic than that), and of course you can do the same as in my example just with "when String,Integer"; the only thing this buys you is the ability to pass the list around as an object:

classTypeUniondefinitialize(*args)@types=argsenddef===(arg)@types.detect{|t|t===arg}endenddeftypeunion(*args)returnTypeUnion.new(*args)enddeftest(arg)caseargwhentypeunion(String,Integer)puts"String or Integer:#{arg}"elseputs"What's this?#{arg.inspect}"endendtest(4)test("foo")test(1.0)test(false)

As for your suggestion formultiple dispatch, I don't think it's a hard change at all. I'm working on a Ruby compiler, and the way I'd implement something like that would simply be to create hidden methods with the type signature for rewrites at call-sites where I can prove the types at compile time, and make the "plain" method a generated method that does the type checks and dispatches to each implementation. Conceptually it's really just wrapping every variant in a big "case", and not hard at all.

The hard part is getting people to agree to add it.

CollapseExpand
 
katafrakt profile image
Paweł Świątkowski
  • Location
    Wrocław, Poland
  • Pronouns
    he/him
  • Work
    Elixir and ReScript developer @ Walnut
  • Joined

Thanks for giving a proper name for what I'm looking for. Neither pattern matching nor method overload mentioned above did not seem correct. Multiple dispatch clicks.

Perhaps you're right that it soudn't be so hard to implement. But wouldn't it break backwards compatibility somehow? Ruby core team is known for keeping it at all costs.

CollapseExpand
 
vidarh profile image
Vidar Hokstad
  • Joined

It would break semantics if overloads are allowed in a way that is syntactically valid in older code. So the challenge would be to pick syntax to specify the type signatures that can not occur in old working code.

Finding syntax that doesn't look awful but that is currently invalid would actually likely be the hardest part of adding support for it.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Location
    Wrocław, Poland
  • Pronouns
    he/him
  • Work
    Elixir and ReScript developer @ Walnut
  • Joined

More fromPaweł Świątkowski

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp