Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

What's especially nice about this, is that it can be explained as a more or less syntactic transformation. You don't even need a special keyword, just a rule that method bodies beginning with `in PATTERN` are wrapped in an implicit `case [*args, kwargs.any? ? kwargs : nil].compact ... end` expression.

Your example method is a bit tricky, because it rebinds an argument and then presumably uses it in the elided bit (the "..."). If the case statement was the entire method body, given proposed syntactic rule it could be written as:

    def for_environment
    in String => code unless code in UUID_RE
      none # no support for filtering via environment codes
    in UUID_RE => id
      Environment.find_by(id:) || none
    in Environment => env
      env
    in nil
      nil
    end
To me this "rhymes" nicely with the ability to write `rescue` blocks at the method level:

    def foo(params)
      send_request(params)
    rescue => e
      raise if critical_error?(e)
    end
---

That is of course a pretty rough approximation, and the real rules would probably be a bit more complex[0], but it definitely seems achievable and possibly worth doing even without taking the type-annotation aspect into account.

[0]: for a start, patterns would need to be extended to allow binding the block argument in a manner equivalent to `def foo(&block)`



I think introducing defp, with similar semantics to Elixir -- i.e. with overloading -- would actually be quite beneficial, while still feeling like Ruby.

For example, with overloading,

    defp for_environment(Environment => environment)
      case
      when environment.isolated?
        where(environment:)
      when environment.shared?
        where(environment: [nil, *environment])
      else
        none
      end
    end

    defp for_environment(nil)
      where(environment: nil)
    end

    defp for_environment(String => code unless code in UUID_RE)
      none # no support for filtering via environment codes
    end

    defp for_environment(UUID_RE => id)
      return none unless environment = Environment.find_by(id:)

      # calls the pattern matched method for Environment
      for_environment(environment)
    end
Which would be equivalent to (using syntax from OP),

    defp for_environment
    in String => code unless code in UUID_RE
      none # no support for filtering via environment codes
    in UUID_RE => id
      return none unless environment = Environment.find_by(id:)

      case
      when environment.nil?
        where(environment: nil)
      when environment.isolated?
        where(environment:)
      when environment.shared?
        where(environment: [nil, *environment])
      else
        none
      end
    in Environment => environment
      case
      when environment.isolated?
        where(environment:)
      when environment.shared?
        where(environment: [nil, *environment])
      else
        none
      end
    in nil
      where(environment: nil)
    end
Which would be equivalent to,

    def for_environment(environment)
      environment =
        case environment
        in String => code unless code in UUID_RE
          return none # no support for filtering via environment codes
        in UUID_RE => id
          return none unless env = Environment.find_by(id:)

          env
        in Environment => env
          env
        in nil
          nil
        end

      case
      when environment.nil?
        where(environment: nil)
      when environment.isolated?
        where(environment:)
      when environment.shared?
        where(environment: [nil, *environment])
      else
        none
      end
    end
In this case, I feel like defp with overloading could really clean up the code (defp without overloading, not so much). You no longer have to add the input transformation to your mental stack when groking the method.

I also don't really see a problem with allowing &block like a normal def.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: