Classes | Libraries > JITLib > Environments | Live Coding

Maybe : Ref : AbstractFunction : Object

referentially transparent proxy object
Source: Fdef.sc
Subclasses: Fdef

Description

A Maybe object can contain either nil or some other object, and allows to construct calculations without knowing this other object yet. If the calculation fails, due to a loop or a not yet defined object, Maybe returns nil.

The name Maybe stems from the programming language Haskell, where it represents a somewhat similar entity. See also: Fdef

NOTE: A Maybe object lazily constructs an operation as an object instead of performing it immediately. Its implementation remains incomplete necessarily, in the current form: it is limited to the arithmetic operations that AbstractFunction implements and the following collection methods: put, addAll, add, putAll, atAll.

You can extend Maybe as you need for your purpose, by the following schema:

// replaye "myMethod" with your method name
+ Maybe {
    myMethod { |...args|
        ^this.composeNAryOp(\myMethod, args)
    }
}

Class Methods

Maybe.new(thing)

From superclass: Ref

create a new instance

Arguments:

thing

an object or nil.

a = Maybe.new;
b = Maybe(a + 6);
b.value; // => nil
a.value = 1;
b.value; // => 7

Inherited class methods

Undocumented class methods

Maybe.callFunc

Maybe.callFunc = value

Maybe.callers

Maybe.current

Maybe.defaultValue

Maybe.defaultValue = value

Maybe.protected

Maybe.protected = value

Maybe.verbose

Maybe.verbose = value

Instance Methods

.source

.source = obj

return or set the contained object

.value

.value = value

set the contained object or return the source, or the value of the contained object, if it is a Maybe. If there is a recursion, return nil.

.apply( ... args)

return the value, or the value of the contained object, if it is a Maybe. This method allows recursion, so that recursive calculations can be made.

.doesNotUnderstand(selector ... args)

(called by any message that Maybe doesn't understand.)

returns a composition function that, when evaluated, returns the value.

a = Maybe.new;
a.respondsTo(\flop) // false: Maybe constructs a placeholder instead
b = Maybe(a.flop);
b.value; // => nil
a.value = [1, 2, [2, 3]];
b.value;    // => [ [ 1, 2, 2 ], [ 1, 2, 3 ] ]

Inherited instance methods

Undocumented instance methods

<>(that)

.add( ... args)

.addAll( ... args)

.all

.at( ... args)

.atAll( ... args)

.catchRecursion(func)

.clear

.composeBinaryOp(aSelector, something, adverb)

.composeNAryOp(aSelector, anArgList)

.composeUnaryOp(aSelector)

.do(function)

.embedInStream(inval)

.findKey

.functionPerformList(selector, arglist)

.includedInCallers

.infoString(args)

.o( ... args)

.postString

.put( ... args)

.putAll( ... args)

.reduceFuncProxy(args, protect: true)

.reverseComposeBinaryOp(aSelector, something, adverb)

.valueArray(args)

.valueArrayEnvir( ... args)

.valueEnvir( ... args)

.valueFuncProxy(args)

Examples

// the following examples use a LazyEnvir with a Maybe as a proxy class.
// instead of writing a = Maybe.new; a.value = something;
// one can simply write ~a = something.
// the Maybe is implicitly created for you.

(
    p.pop.clear;
    p = LazyEnvir.new;
    p.proxyClass = Maybe;
    p.linkDoc; // here: connect to current doc only.
);


// sets
~a = Set[0, 4, 5, 7];
~b = Set[4, 5];
~c = ~a union: ~b; // union of the two sets (note that the shortcut | does not work here.).
~d = ~a sect: ~b; // intersection of a and b
~c.postcs;""; // post the whole construction
~d.postcs;"";
~c.value; // Set[ 4, 0, 5, 7 ]
~d.value; // Set[ 4, 5 ]
~b = Set[4, 5, 13, 0];
~c.value;
~d.value; // Set[ 4, 0, 5 ]
~b.source.add(~w); // add another placeholder
~c.value; // it is part of the union.
~d.value; // but not part of the intersection


// envirs
~a = (note: [1, 2]);
~b = (dur: 1);
~c = ~a.putAll(~b) // provisionally put everything into the placholder
~c.value;
~a = (note: [1, 2, 4]);
~c.value;
~d = ~a.at(\note);
~d.value;
~a = (note: [7.5]);
~d.value; // [7.5]

// patterns
~a = Pseq([1, 2, 3]);
~b = Pseq([5, ~a, ~a + 10], inf);
~b.value.asStream.nextN(10);


~a = Prand([100, 200]);
~b.value.asStream.nextN(10);


// to do : flop!

//////////////// deep recursion

// with normal functions:
f = { |x| if(x <= 1) { 1 } { x * f.(x - 1) } };
f.(12)


~faculty = { |x| if(x == 1) { 1 } { x * ~faculty.(x - 1) } };
~faculty.(12) // doesn't work (=> nil). here we _do_ want recursion ...

// for explicit recursion use "apply"
~faculty = { |x| if(x == 1) { 1 } { x * ~faculty.apply(x - 1) } };
~faculty.(12)

/*// safety (not yet implemented)
Maybe.maxDepth = 1e2; // higher depth is risky..
~faculty = { |x| x * ~faculty.apply(x - 1) }; // infinite recursion
~faculty.(12)

Maybe.maxDepth = nil; // unsafe again.*/


//////////////// recursion prevention tests

~b = ~a;
~a = ~b;
~a.value; // => nil


~a = ~b;
~b = ~c;
~c = ~a;
~a.value; // => nil

~a = ~b + ~c;
~c = ~a;
~a.value; // => nil


~a = ~b;
~b = 19;
~a.value; // => 19
~b.value; // => 19

// function evaluation and argument passing

~a = { |x| x + 2 };
~a.value; // => nil

~a.value(~c); // => nil
~b = 2000;
~a.value(~b); // => 2002
~x = [600, 1000];

(~a + 1).value(~b); // 2003
(~a + 1).value(~x); // [ 603, 1003 ]
(~a + 1).value({ 8 }); // binary op func.
(~a + 1).value({ 5 + 3 }).value // 11

~a = { |x| x + 2 + ~b };
~a.value(8); // 2010

~c = nil;
~a = { |x| x + 2 + ~c }; // ~c is undefined.
~a.value(8); // => nil

~c = 100; // define ~c

~a.value(8); // now returns a value.

~c = ~b; // now recursion?
~b = ~a;
~a.value(8); // caught recursion => nil

~c = { 100.rand }; // ~c is a function

~a.value(8);
~a.value(8);

~c = { ~a + ~b };
~a.value(8);    // ~c is a recursion with ~a => nil


// function composition
~a = {|x| x + 1 };
~v = ~a <> ~a <> ~a; // same as: { ~a.(~a.(~a)) }
~v.value(0); // => 3

~a = {|x| x + 2 };
~v.value(0); // transparent. => 6

// {|x| x }.valueEnvir // doesn't work with current implementation of Function:valueEnvir


// calculations with functions:
~c = 0;
~a = { |ff| { ff = ff + 1; ~c + ff + 2 + ~c } };
~x = ~a.value(8);
~x.value; // return 11, 12, 13...
~x.value;
~x.value;
~c = 100;
~x.value; // return 214, 215 ...
~x.value;

// binary op functions:
~c = 0;
~a = { |ff| { [600, 800] } + { ff + 2 + ~c } };

~x = ~a.value(8);
~x.value; // return [ 610, 810 ]

~c = { [10, -10].rand };
~x.value; // return random between [ 610..620, 800..810 ]