Class: NRSER::Types::Where

Inherits:
Type show all
Defined in:
lib/nrser/types/where.rb

Overview

Where instances are predicate functions¹ as a type.

They have a #predicate block, and #test? calls it with values and returns the boolean of the result (double-bangs it ‘!!`).

Super simple, right? And easy! Why don’t we just use these things all over the place?

If you’re been around the programing block a few times, you probably saw this coming a mile away: you should avoid using them.

Yeah, sorry. Here’s the reasons:

  1. They’re opaque - it’s hard to see inside a Proc… even if you got the source code (which seems like it requires gems and involves some amount of hackery), that wouldn’t really give you the whole picture because you need to look at the binding as well… Ruby procs capture their entire environment.

    Essentially, they suck to easily and/or comprehensively communicate what they hell they do.

  2. Like When, they’re totally Ruby-centric… we can’t really serialize them and pass them off anywhere, so they’re shitty for APIs and property types and stuff that you may want or need to expose outside the runtime.

    In this sense they’re ok as implementations of types like file_path that represent an idea to be communicated to the outside world, where each system that handles that idea will need to have it’s own implementation of it.

    Lit addresses a lot of this with serializable functions, but that’s nowhere near ready to rock, and support for it would probably be added along side Where, not in place of it (since Where is probably still going to be used and useful).

So please be aware of those, and be reasonable about your Wheres.

> ¹ I say functions, because they really should be functions (same > input always gets same output, pure, etc.). > > Yeah, there’s not much stopping you from making them state-based or > random or whatever, but please don’t do that shit unless you’ve really > thought it through. And if you still do, please write me and tell me > what you thought and why it’s a reasonable idea and I’ll update this.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Type

#===, #builtin_inspect, #check, #check!, #default_name, #default_symbolic, #from_data, #from_s, #has_from_data?, #has_from_s?, #has_to_data?, #inspect, #intersection, #name, #not, #respond_to?, #symbolic, #test, #to_data, #to_proc, #to_s, #union, #xor

Constructor Details

#initialize(method, **options) ⇒ Where #initialize(**options, &block) ⇒ Where

Note:

Documentation and examples are indented to illustrate behavior and aid in development. Please use the factory method Types.where to create instances - they allow us to easily improve and optimize

Make a new NRSER::Types::Where.

Overloads:

  • #initialize(method, **options) ⇒ Where

    This is the preferred form, where a bound method provide the membership predicate.

    Class or module methods make the most sense, though this is not enforced.

    Examples:

    Create a Type that tests if a path is a file

    type = Where.new File.method( :file? )

    Parameters:

    • method (Method<(Object)=>Boolean>)

      Arity 1 bound method that will be used to decide membership (if it responds truthy then the argument is a member of the type).

    • options (Hash)

      Additional options that will be passed up to Type#initialize.

  • #initialize(**options, &block) ⇒ Where

    This form should be used sparingly - please use a bound class or module Method instead of an opaque ‘&block` for the predicate.

    it exists mostly for legacy reasons, and for the

    Parameters:

    • name (String)

      In this form, a ‘name` is required because it is not usually possible to extract any descriptive information from the `&block`.

    • options (Hash)

      Additional options that will be passed up to Type#initialize.

    • block (Proc<(Object)=>Boolean>)

      Arity 1 Proc that will be used to decide membership (if it responds truthy then the argument is a member of the type).



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/nrser/types/where.rb', line 123

def initialize method = nil, **options, &block
  # Check up on what we got

  if method && block
    raise NRSER::ArgumentError.new \
      "Can't supply both method", method, "(first arg)",
      "and &block", block
    
  elsif !method && !block
    raise NRSER::ArgumentError.new \
      "Must provide either a Method<(Object)=>Boolean> as the first argument",
      "*or* a Proc<(Object)=>Boolean> as the block"
    
  end

  @predicate = method || block
  
  unless predicate.arity == 1
    raise NRSER::ArgumentError.new \
      "{NRSER::Types::Where} predicates must have arity 1",
      predicate: predicate,
      arity: predicate.arity,
      options: options
  end

  unless options[:name]
    if predicate.is_a?( Method )
      options[:name] = predicate.full_name
    else
      raise NRSER::ArgumentError.new \
        "`name:` keyword argument is required when creating {Where}",
        "from `&block` predicates."
    end
  end

  super **options

end

Instance Attribute Details

#predicateProc<(V) => Boolean> (readonly)

Predicate Proc used to test value for membership.

Returns:

  • (Proc<(V) => Boolean>)

    Really, we double-bang (‘!!`) whatever the predicate returns to get the result in #test?, but you get the idea… the response will be evaluated on its truthiness.



75
76
77
# File 'lib/nrser/types/where.rb', line 75

def predicate
  @predicate
end

Instance Method Details

#explainString

A string that is supposed to give our best concise description of the type.

NRSER::Types::Where sucks because we can’t really do much here.

Returns:



180
181
182
# File 'lib/nrser/types/where.rb', line 180

def explain
  "#{ self.class.demod_name }<#{ @name }>"
end

#test?(value) ⇒ Boolean

Test a value for membership.

Parameters:

  • value (Object)

    Value to test for type satisfaction.

Returns:

  • (Boolean)

    ‘true` if the `value` satisfies the type.

Raises:



169
170
171
# File 'lib/nrser/types/where.rb', line 169

def test? value
  !!@predicate.call( value )
end