Sequel gem identity / natural element

44 views Asked by At

I am trying to use a reduce function to &-connect multiple sequel expressions. However in the beginning I'd have the need for a natural element / identity element Sequel-expression. i.e. an expression that can be &-connected without changing the meaning of the query. I tried just using nil, but that does not seem to work. Is there such a thing?^^

Imagine the following

def reduce(buffer, array, &func)
  return buffer if array.empty?

  if buffer.nil? # I basically want to get rid of this
    reduce(array[0], array[1..-1], &func)
  else
    reduce(func.call(buffer, array[0]), array[1..-1], &func)
  end
end

# to be able to call:
reduce(NATURAL_ELEMENT, array_of_expressions) { |first, second| first & second }

When calling Sequel.expr(nil) I get a #<Sequel::SQL::Wrapper @value=>nil> but I don't know exactly what effect that has. Is this what I am looking for?

2

There are 2 answers

0
Drenmi On

What you want is a universal set, i.e. a set containing all possible elements. This is the identity for set intersections. If you use Sequel.expr(nil), you'll have the equivalent of an empty set.

I'm not aware of any universal set (or other identity object) in Sequel, but you could try to implement one. Here's an example (using the Singleton module for good measure):

require "singleton"

class UniversalSet
  include Singleton

  def &(other)
    other
  end
end

UNIVERSAL_SET = UniversalSet.instance

Now you can use this in set intersection operations. The type of the elements is irrelevant, so it works fine for SQL objects, as well.

UNIVERSAL_SET & [1, 2, 3]
#=> [1, 2, 3]

If you want to, you can also implement the other set operations for the universal set.

This, however, has the downside that it is not commutative. If you try the inverse:

[1, 2, 3] & UNIVERSAL_SET

it will throw an error. This is a limitation in the available coercion constructs in Ruby. (Currently you can only overload operators commutatively for numbers.)

0
Eric Duminil On

Here's your something for which something & Sequel.expr(field: 1) == Sequel.expr(field: 1):

class IdentityFunction
   def self.&(b)
    b
  end
end

IdentityFunction & "test" #=> "test"
IdentityFunction & 3 #=> 3

You can write :

[[1,2,3,4,5], [2,3,4], [3,4,5]].reduce(IdentityFunction){|a,b| a & b} #=> [3, 4]

Please note that reduce takes the first element as start value by default, so IdentityFunction isn't even needed :

[[1,2,3,4,5], [2,3,4], [3,4,5]].reduce{|a,b| a & b} #=> [3, 4]

Which means what you want to achieve could probably be written as :

array_of_expressions.reduce{ |first, second| first & second }

or just

array_of_expressions.reduce(&:&)