Feature #6806

Support functional programming: forbid instance/class variables for ModuleName::method_name, allow for ModuleName.method_name

Added by Alexey Muranov about 3 years ago. Updated about 1 year ago.

[ruby-core:46829]
Status:Feedback
Priority:Normal
Assignee:Yukihiro Matsumoto

Description

What would you say about this proposal? Is there a better alternative?

I suggest to support functional programming in Ruby by making module methods called with ModuleName::method_name syntax raise an exception if the method uses instance or class variables (instance variables of the singleton class, of course).
If i understand correctly, currently ModuleName::method_name and ModuleName.method_name behave identically, so i propose that they be different:

module M
  module_function
    def f(x)
      x*x
    end
    def g(x)
      @x ||= x
      @x*@x
    end
end

M.f(2) # => 4
M.g(2) # => 4
M::f(3) # => 9
M::g(3) # => Error: instance variable `@x` used in a functional call `M::g`

Current behavior:

M.f(2) # => 4
M.g(2) # => 4
M::f(3) # => 9
M::g(3) # => 4

History

#1 Updated by Alexey Muranov about 3 years ago

I think that if you tell a new person that M::f and M.f are the same in Ruby, they won't believe at first. So why keeping them the same?

I do not know how to be with the use of global variables in M::f. I imagine there are other details to discuss.

This feature wouldn't ensure truly functional programming, but i hope it can introduce a "good practice" of making it clear from the call syntax if a method can change the state of the object. In other words, i propose that when x::f(y) is called, the state of the object x be frozen, and unfrozen after the return from f, so x act simply as a namespace.

I do not know whether it would be better for x::f to allow read-only access to the state of x, or no access at all. Probably read-only access makes better sense, given impossibility to exclude interaction with other data anyway.

Another approach could be to forbid a function called with :: prefix to change the state of any object that is not created by it, and to forbid to read the state of any object that is not accessible through the state of self or the state of one of the arguments.

If the feature is implemented with read-only access to any existing data, this syntax can be used to give a precise meaning to non-exclaiming methods: a::map instead of a.map, and a.map! stays as is.

To determine what objects are "accessible" through the state of a given object, i think it should be forbidden to go "up" with methods like "#class" or "#superclass" and then go "down". In the direction "up", only the object's class and its ancestors should be accessible, i think.

#2 Updated by Eric Hodel about 3 years ago

  • Assignee set to Yukihiro Matsumoto
  • Target version set to Next Major

This would break compatibility with much ruby code.

#3 Updated by Alexey Muranov about 1 year ago

Besides functional programming, IMO this would support command–query separation.

#4 Updated by Nobuyoshi Nakada about 1 year ago

  • Description updated (diff)
  • Status changed from Open to Feedback

It seems unrelated to "functional programming" at all.

#5 Updated by Alexey Muranov about 1 year ago

I meant that a function called like Math::sin would be required to return same values (for same arguments) every time. Maybe i did not explain this well. Foo.bar, on the other hand, would not have this restriction.

Same could be generalized to mutable (non-constant) objects, but there the distinction could be harder to make. Maybe x::to_s but x.update!?

P.S. Thanks for reformatting the proposal.

#6 Updated by Nobuyoshi Nakada about 1 year ago

It's just your style.
I use Math.sin and so on.

Also available in: Atom PDF