ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
From: JoergWMittag+Ruby-Lang@GMail.Com
To: ruby-core@ruby-lang.org
Subject: [ruby-core:69590] [CommonRuby - Feature #11262] [Open] Make more objects behave like "Functions"
Date: Mon, 15 Jun 2015 11:55:05 +0000	[thread overview]
Message-ID: <redmine.issue-11262.20150615115504.a350a00b3ec0a480@ruby-lang.org> (raw)
In-Reply-To: redmine.issue-11262.20150615115504@ruby-lang.org

Issue #11262 has been reported by Jörg W Mittag.

----------------------------------------
Feature #11262: Make more objects behave like "Functions"
https://bugs.ruby-lang.org/issues/11262

* Author: Jörg W Mittag
* Status: Open
* Priority: Low
* Assignee: 
----------------------------------------
# What is a Function?

In Ruby, we have the `Proc` class to represent objects which are "function-like". But, in true object-oriented / duck-typing fashion, an object doesn't actually have to be an instance of `Proc` in order to be treated as a function, it only needs to respond to `call`. For cases, where a `Proc` instance is absolutely required (mostly, the `&` unary prefix ampersand "make-me-a-block" operator), there is the `to_proc` conversion.

So, in short: if an object wants to be a function, it must respond to `call`, and should also respond to `to_proc`.

There are some objects in Ruby that *could* be seen as functions, but currently don't respond to `call` or `to_proc`:

# `Array` as mapping

An array is a mapping from indices to elements. "Mapping" is just a different word for (partial) function, though! I propose, that `Array` should implement `call` and `to_proc` in the following manner:

    class Array
      alias_method :call, :[]

      def to_proc
        method(:call).to_proc
      end
    end

# `Hash` as mapping

A hash is a mapping from keys to values. I propose, that `Hash` should implement `call` and `to_proc` in the following manner:

    class Hash
      alias_method :call, :[]

      def to_proc
        method(:call).to_proc
      end
    end

Note the duplication here. This suggests maybe refactoring to break out an `Indexable` mixin that is included by both `Array` and `Hash`. However, this is out of scope of this particular proposal.

# `Set` as predicate

A set is a mapping from values to booleans, i.e. a set is the same as its `include?` predicate. This would mean, for example, that I can pass a `Set` as a predicate to methods like `Enumerable#select`. I propose, that `Set` should implement `call` and `to_proc` in the following manner:

    require 'set'

    class Set
      alias_method :call, :include?

      def to_proc
        method(:call).to_proc
      end
    end

I believe that these three additions are worthwhile and fairly uncontroversial. They match with the way arrays, maps and especially sets are treated in mathematics and in other programming languages. E.g. in both [Clojure](http://clojure.org/data_structures#Data%20Structures-Maps%20(IPersistentMap)) and [Scala](http://scala-lang.org/api/current/#scala.collection.Seq), arrays, sets and maps are functions and use function application syntax for accessing values. Scala doesn't even have indexing syntax.

Here are some potential use cases:

    numbers_to_words = %w[zero one two three four five six seven eight nine ten eleven twelve]

    [4, 7, 1, 0, 8].map(&numbers_to_words)
    # => ['four', 'seven', 'one', 'zero', 'eight']


    allowed_languages = Set[:ruby, :python, :scala, :scheme]

    %i[ruby c cplusplus scala java perl].select(&allowed_languages)
    # => [:ruby, :scala]

Here is a more "wild" proposal that is much more controversial. I don't actually propose adding this to Ruby, but I will mention it here as food for thought:

# `Class` as factory

If you squint your eyes, tilt your head sideways and look at it juuuuuuust right, a class is a factory for objects. In other words, it is a function from constructor arguments to instances:

    class Class
      alias_method :call, :new

      def to_proc
        method(:call).to_proc
      end
    end

Example:

    class Person
      def initialize(name)
        @name = name
      end
    end

    %w[matz ko1 charlie].map(&Person)
    # => [#<Person:0x007fe50ec19698 @name="matz">, #<Person:0x007fe50ec19648 @name="ko1">, #<Person:0x007fe50ec195f8 @name="charlie">]

# Incompatibilities

This proposal conflicts with #10829.

I believe that having `Array`s behave as functions from indices to elements is natural, unsurprising, and well in line with both mathematics and other languages.

# Related

The code duplication encountered here suggests refactoring to extract two new mixins in the Ruby core library:

    module Callable
      def to_proc
        method(:call).to_proc
      end
    end

    module Indexable
      alias_method :call, :[]
    end

However, this is out of scope for this particular feature request.



-- 
https://bugs.ruby-lang.org/

       reply	other threads:[~2015-06-15 11:33 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <redmine.issue-11262.20150615115504@ruby-lang.org>
2015-06-15 11:55 ` JoergWMittag+Ruby-Lang [this message]
2015-07-09  5:10 ` [ruby-core:69906] [CommonRuby - Feature #11262] Make more objects behave like "Functions" 2851820660
2016-02-22 15:27 ` [ruby-core:73919] [Ruby trunk Feature#11262] " Ruby-Lang
2016-02-22 16:50   ` [ruby-core:73927] " Hanlyu Sarang
2016-02-22 16:53     ` [ruby-core:73928] " Recursive Madman
2016-02-22 15:46 ` [ruby-core:73922] " zverok.offline
2016-02-23  0:55 ` [ruby-core:73939] " Ruby-Lang
2016-02-23  0:58 ` [ruby-core:73940] " Ruby-Lang
2016-06-24  8:40 ` [ruby-core:76136] " Ruby-Lang
2016-06-24  8:43 ` [ruby-core:76137] " Ruby-Lang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.ruby-lang.org/en/community/mailing-lists/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=redmine.issue-11262.20150615115504.a350a00b3ec0a480@ruby-lang.org \
    --to=ruby-core@ruby-lang.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).