243 lines
5 KiB
Racket
243 lines
5 KiB
Racket
#lang scribble/manual
|
|
@(require (for-label racket))
|
|
|
|
@title{Maybe}
|
|
referentially transparent proxy object@section{categories}
|
|
Libraries>JITLib>Environments, Live Coding
|
|
@section{related}
|
|
Classes/Fdef, Overviews/JITLib
|
|
|
|
@section{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 strong::Maybe:: stems from the programming language Haskell, where it represents a somewhat similar entity. See also: link::Classes/Fdef::
|
|
|
|
@section{ClassMethods}
|
|
|
|
|
|
@section{method}
|
|
new
|
|
create a new instance
|
|
|
|
@section{argument}
|
|
thing
|
|
an object or nil.
|
|
|
|
@racketblock[
|
|
a = Maybe.new;
|
|
b = Maybe(a + 6);
|
|
b.value; // => nil
|
|
a.value = 1;
|
|
b.value; // => 7
|
|
::
|
|
|
|
]
|
|
@section{InstanceMethods}
|
|
|
|
|
|
@section{method}
|
|
source
|
|
return or set the contained object
|
|
|
|
@section{method}
|
|
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.
|
|
|
|
@section{method}
|
|
apply
|
|
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.
|
|
|
|
@section{method}
|
|
doesNotUnderstand
|
|
(called by any message that Maybe doesn't understand.)
|
|
|
|
returns a composition function that, when evaluated, returns the value.
|
|
|
|
@racketblock[
|
|
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 ] ]
|
|
::
|
|
|
|
]
|
|
@section{Examples}
|
|
|
|
|
|
|
|
@racketblock[
|
|
// 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 ]
|
|
::
|
|
]
|
|
|
|
|