Coding: Fleeting Thoughts

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

MostlyHarmless
Posts: 154
Joined: Sun Oct 22, 2006 4:29 am UTC

Re: Coding: Fleeting Thoughts

Postby MostlyHarmless » Wed Jul 29, 2015 1:04 am UTC

@Flumble: I'm not entirely sure what you're asking, but I think you're looking for

Code: Select all

resCell = arrayfun(funcPtr, argList..., 'UniformOutput', False)

(Alternatively, you could try cellfun.)

As a side note, why do you hate anonymous functions? I can understand trying not to confuse students (I teach an undergraduate Matlab class, and I try to postpone anonymous functions until they've gotten pretty comfortable with the basics first), but they're extremely useful.

User avatar
The Great Hippo
Swans ARE SHARP
Posts: 7368
Joined: Fri Dec 14, 2007 4:43 am UTC
Location: behind you

Re: Coding: Fleeting Thoughts

Postby The Great Hippo » Wed Jul 29, 2015 2:30 am UTC

I recently discovered class swapping; in Python, you can change an object's type by assigning its __class__ to another class. Which is both interesting and horrifying. Apparently, there are some interesting use-cases for this -- particularly when dealing with objects explicitly designed for swapping classes with each other.

Regardless, the instant I found out about it, my first instinct was to try and see if I can swap the type of a dictionary key into a mutable type, in place.

(So far, no luck. You can't assign built-in types to __class__, and trying to fool it by using a blank custom class that inherits from a built-in mutable doesn't work. However, I suspect I can make it work if I just build my own mutable type.)

letterX
Posts: 535
Joined: Fri Feb 22, 2008 4:00 am UTC
Location: Ithaca, NY

Re: Coding: Fleeting Thoughts

Postby letterX » Wed Jul 29, 2015 6:52 am UTC

Flumble wrote:Lastly, it took me a day to find out matlab does have a function to get the magnitude of a vector: norm. It was neither length nor size and magnitude doesn't exist at all. Nor do normalize or unit exist for normalizing a vector. :cry:

So, it used to mystify me why matlab doesn't have a normalize function, but then I realized that all matlab functions take arguments by value (technically, copy on write) so it's impossible to write a normalize function that modifies its argument to have length 1.

On the list of things that infuriate me about matlab, this is towards the bottom, actually...

User avatar
Qaanol
The Cheshirest Catamount
Posts: 3069
Joined: Sat May 09, 2009 11:55 pm UTC

Re: Coding: Fleeting Thoughts

Postby Qaanol » Wed Jul 29, 2015 7:38 am UTC

Matlab does support in-place operations.
wee free kings

User avatar
Flumble
Yes Man
Posts: 2264
Joined: Sun Aug 05, 2012 9:35 pm UTC

Re: Coding: Fleeting Thoughts

Postby Flumble » Wed Jul 29, 2015 11:36 am UTC

MostlyHarmless wrote:@Flumble: I'm not entirely sure what you're asking, but I think you're looking for

Code: Select all

resCell = arrayfun(funcPtr, argList..., 'UniformOutput', False)

(Alternatively, you could try cellfun.)

Thanks. I'm a little confused as to why matlab would be more restrictive by default, but it can't be helped I guess.

MostlyHarmless wrote:As a side note, why do you hate anonymous functions? I can understand trying not to confuse students (I teach an undergraduate Matlab class, and I try to postpone anonymous functions until they've gotten pretty comfortable with the basics first), but they're extremely useful.

Mostly, because their syntax consists of <token for a function pointer><argument list><expression>. There's no end token, there's no way to specify your output. It's concise, but obscure.
Moreover, I just learned that variables (that are not function arguments) inside an anonymous function are replaced by their value during the declaration, not during evaluation nor during compilation (the last is probably because there's no such thing as compiling). I would be OK with this if matlab were a pure language, but now it's just inconsistent with normal functions.

letterX wrote:
Flumble wrote:Lastly, it took me a day to find out matlab does have a function to get the magnitude of a vector: norm. It was neither length nor size and magnitude doesn't exist at all. Nor do normalize or unit exist for normalizing a vector. :cry:

So, it used to mystify me why matlab doesn't have a normalize function, but then I realized that all matlab functions take arguments by value (technically, copy on write) so it's impossible to write a normalize function that modifies its argument to have length 1.

On the list of things that infuriate me about matlab, this is towards the bottom, actually...

I was thinking of a normalize function that accepts a vector anything and returns a normalized copy. Even if matlab replaces it with a/norm(a), it would be better than writing your own slow function.

User avatar
Qaanol
The Cheshirest Catamount
Posts: 3069
Joined: Sat May 09, 2009 11:55 pm UTC

Re: Coding: Fleeting Thoughts

Postby Qaanol » Wed Jul 29, 2015 4:13 pm UTC

Code: Select all

function x = normalize(x)
x = x ./ norm(x);


Or if you’re feeling fancy:
Spoiler:

Code: Select all

function x = normalize(x, p)
if ~exist('p')
    p = 2;
end
x = x ./ norm(x, p);
As long as you call this from inside another function, and assign its result to the same variable as you put in, then it will transform x in place.

It’s actually a rather clever syntactic design: there are never any “invisible” changes to variables, because the only way to modify a value is to assign something to it. And yet, if a function has a return value with the same name as an input parameter, then the caller of that function may opt in to in-place operation by assigning that return value to the same variable that was passed as the corresponding argument.

I do not know if this works for subarrays though. As in, if you have a matrix M and call something like “M(1,:) = normalize(M(1,:))” or “M(:,1) = normalize(M(:,1))” I do not know whether it will operate in place.
wee free kings

MostlyHarmless
Posts: 154
Joined: Sun Oct 22, 2006 4:29 am UTC

Re: Coding: Fleeting Thoughts

Postby MostlyHarmless » Wed Jul 29, 2015 8:53 pm UTC

Flumble wrote:
MostlyHarmless wrote:@Flumble: I'm not entirely sure what you're asking, but I think you're looking for

Code: Select all

resCell = arrayfun(funcPtr, argList..., 'UniformOutput', False)

(Alternatively, you could try cellfun.)

Thanks. I'm a little confused as to why matlab would be more restrictive by default, but it can't be helped I guess.


I suspect this is mainly because the output needs to be a cell if it contains different types. If the output is a cell (or potentially worse, if it may or may not be a cell) then it's very easy to get an error by using resCell{1} instead of resCell(1) or vice versa.

Flumble wrote:
MostlyHarmless wrote:As a side note, why do you hate anonymous functions? I can understand trying not to confuse students (I teach an undergraduate Matlab class, and I try to postpone anonymous functions until they've gotten pretty comfortable with the basics first), but they're extremely useful.

Mostly, because their syntax consists of <token for a function pointer><argument list><expression>. There's no end token, there's no way to specify your output. It's concise, but obscure.
Moreover, I just learned that variables (that are not function arguments) inside an anonymous function are replaced by their value during the declaration, not during evaluation nor during compilation (the last is probably because there's no such thing as compiling). I would be OK with this if matlab were a pure language, but now it's just inconsistent with normal functions.


That's fair; it is an odd way of setting things up. It's still probably better to eventually get used to them (and, more importantly, to get your student used to them).

korona
Posts: 495
Joined: Sun Jul 04, 2010 8:40 pm UTC

Re: Coding: Fleeting Thoughts

Postby korona » Wed Jul 29, 2015 9:19 pm UTC

If I ever have to argue with someone if C is a better language for low level programming than C++ I will point them to glibc's runtime dynamic linker implementation that uses a combination of includes and macros to handle different ELF formats somewhat generically.

User avatar
Flumble
Yes Man
Posts: 2264
Joined: Sun Aug 05, 2012 9:35 pm UTC

Re: Coding: Fleeting Thoughts

Postby Flumble » Wed Jul 29, 2015 9:47 pm UTC

MostlyHarmless wrote:I suspect this is mainly because the output needs to be a cell if it contains different types. If the output is a cell (or potentially worse, if it may or may not be a cell) then it's very easy to get an error by using resCell{1} instead of resCell(1) or vice versa.

Wait a minute, are you telling me that matlab does have a (somewhat) decent type system? I haven't heard of cells before –can they contain, say, a matrix? How do you access elements/ranges? Can you define a compound (proto)type with them? (I'm self-taught, for lack of a better name, in matlab and never touched any type beyond scalars, vectors, matrices and function handles)


korona wrote:If I ever have to argue with someone if C is a better language for low level programming than C++ I will point them to glibc's runtime dynamic linker implementation that uses a combination of includes and macros to handle different ELF formats somewhat generically.

"Yeah but speeeeed!"
In my more recent opinion, low-level languages only serve to make dumb libraries that allow high-level languages to interface with some hardware non-bootstrapped compilers for high-level languages to be built. Or whatever.

MostlyHarmless
Posts: 154
Joined: Sun Oct 22, 2006 4:29 am UTC

Re: Coding: Fleeting Thoughts

Postby MostlyHarmless » Wed Jul 29, 2015 11:14 pm UTC

Flumble wrote:
MostlyHarmless wrote:I suspect this is mainly because the output needs to be a cell if it contains different types. If the output is a cell (or potentially worse, if it may or may not be a cell) then it's very easy to get an error by using resCell{1} instead of resCell(1) or vice versa.

Wait a minute, are you telling me that matlab does have a (somewhat) decent type system? I haven't heard of cells before –can they contain, say, a matrix? How do you access elements/ranges? Can you define a compound (proto)type with them? (I'm self-taught, for lack of a better name, in matlab and never touched any type beyond scalars, vectors, matrices and function handles)


Cells are essentially arrays without type requirements. Arrays in matlab can only hold one type (and it can only be numeric), but cells can contain any type you want. Quick and dirty examples:
Spoiler:

Code: Select all

>> c = {1, [1 2 3], true, 'a', [1 2; 3 4]}
c =
    [1]    [1x3 double]    [1]    'a'   [2x2 double]
>> c{1}
ans =
     1
>> c{2}
ans =
     1     2     3
>> c{3}
ans =
     1
>> vec = c(2)
vec =
    [1x3 double]
>> vec(3)
Index exceeds matrix dimensions.
>> length(c)
ans =
     5

c{1} is a double, c{2} is a double array, c{3} is a logical, c{4} is a string, etc. However, c(2) is a 1x1 cell containing a double array.

They can also be used like matrices, with all the usual indexing:

Code: Select all

>> c = {1, [1 2 3]; 'a', 'b'}
c =
    [1]    [1x3 double]
    'a'    'b'         
>> c{2}
ans =
a
>> c{1:3}
ans =
     1
ans =
a
ans =
     1     2     3
>> c{1,2}
ans =
     1     2     3
>> size(c)
ans =
     2     2

If you want to loop through them:

Code: Select all

>> c = {1, [1 2 3], 'a'}
>> for i = 1:length(c)
... disp(c{i})
... end
     1
     1     2     3
a

For a slightly more useful example:

Code: Select all

>> c = {[1 2 3], [-3 0 1 5], [1 2; 3 4]};
>> resCell = cellfun(@max, c, 'UniformOutput', false)
resCell =
    [3]    [5]    [1x2 double]
>> resCell{3}
ans =
     3     4

Keep in mind that cells are much slower than arrays whenever both are options. If you don't have to use a cell, then you shouldn't.

(As a side note, I really like that Matlab uses emacs shortcuts, but it's really annoying to switch back and forth when I'm trying to copy-paste things to the forum.)

User avatar
Yakk
Poster with most posts but no title.
Posts: 11129
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Coding: Fleeting Thoughts

Postby Yakk » Fri Jul 31, 2015 5:17 pm UTC

I have lots of problems with C++.

For example, virtual functions and inheritance and virtual inheritance. What they did was pick one *particular* way to implement those concepts, and fix them in the language.

If you want to pick a *different* way to do dynamic dispatch, the language gives you next to no support.

The same is true of methods.

A virtual function table can easily be a run-time concept, but C++ doesn't let you do that. The same system implemented in C (possibly up to the point where it would compile down to the same assembly) easily lets you dynamically create classes with new function overloads at run time. There really isn't a *good reason* (other than making compilers and language writer's jobs easier in a sense) why you couldn't do that in C++ as well, treating classes themselves as data, and "traditional classes" as static/extern instances of such data.

Even more radical implementations of how methods would could also be used. As it stands, `foo->bar(a,b,c)` is shorthand for `Foo::bar( foo, a, b, c)` (where Foo::bar is the member function, and we pass foo as the first argument). If you want to create a non-member function that does something similar, even though at the point of use the compiler has access to the foo pointer, there is no way to get ahold of it within `bar` without having the caller do it explicitly, or needlessly storing an extra copy of Foo within its member.

The language I want is ++C++, in that C++ would be a library within the language that sets up the object model of C++ and what member functions mean and what virtual means and the like, but it would allow you to write a new object model and have people write classes in it *without* having to drop down to the C level.

A concrete example of such a problem can be seen in COM or MFC. COM is basically a slightly different object model than the standard C++ object model, with different ways of creating objects, getting ahold of your "class", invoking method patterns, etc. MFC similarly has a different "method" dispatch mechanism (AFX_MESSAGE_MAP) etc.

Both of them get hacked into C++ with macros, special-purpose code-generating tools, compiler extensions, etc. Basically, the C way of doing things.

Maybe the reflection and synthesis working groups of the C++ standard committee will generate something powerful enough (accidentally -- like templates, or on purpose) to pull off that kind of thing.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Coding: Fleeting Thoughts

Postby Xanthir » Fri Jul 31, 2015 5:58 pm UTC

Related: I'm kinda angry about any type system that can't overload based on return value, too. You have the information *right there* (or can type-infer it)! It's no more ambiguous than dispatching on any other argument! And it lets you write some very concise idiomatic code, like the From trait in Rust, where you can convert from anything to anything with `dest = from(source)`, as long as someone's defined a From::from() overload specialized on the dest and source types.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))

User avatar
Sizik
Posts: 1260
Joined: Wed Aug 27, 2008 3:48 am UTC

Re: Coding: Fleeting Thoughts

Postby Sizik » Fri Jul 31, 2015 6:14 pm UTC

Xanthir wrote:Related: I'm kinda angry about any type system that can't overload based on return value, too. You have the information *right there* (or can type-infer it)! It's no more ambiguous than dispatching on any other argument! And it lets you write some very concise idiomatic code, like the From trait in Rust, where you can convert from anything to anything with `dest = from(source)`, as long as someone's defined a From::from() overload specialized on the dest and source types.


What if you call the method without assigning the return value to something?
she/they
gmalivuk wrote:
King Author wrote:If space (rather, distance) is an illusion, it'd be possible for one meta-me to experience both body's sensory inputs.
Yes. And if wishes were horses, wishing wells would fill up very quickly with drowned horses.

User avatar
ucim
Posts: 6890
Joined: Fri Sep 28, 2012 3:23 pm UTC
Location: The One True Thread

Re: Coding: Fleeting Thoughts

Postby ucim » Fri Jul 31, 2015 6:18 pm UTC

So, say I'm doing a project using OOP. I create an object and a method to do something, but that "something" is expensive. I need to do this thing for all such objects in a certain category; something satisfied by putting them in a loop:

object.thing() // the method
{ connect; // very expensive
do something with this object; // cheap
disconnect; // expensive
}

// now, in the program...
for each object in the set
{ do this expensive thing;
}

But most of the expense is overhead. Say, it's connecting to a distant server. Once connected, I can perform the rest of the thing almost free. In procedural programming I'd just do:

object = some_data_structure; // no methods

// then, in the program...
connect; // very expensive but only done once
for each object in the set
{ do something with this object; // cheap
}
disconnect;

How is this done optimally in OOP?

Jose
Order of the Sillies, Honoris Causam - bestowed by charlie_grumbles on NP 859 * OTTscar winner: Wordsmith - bestowed by yappobiscuts and the OTT on NP 1832 * Ecclesiastical Calendar of the Order of the Holy Contradiction * Heartfelt thanks from addams and from me - you really made a difference.

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Coding: Fleeting Thoughts

Postby Xanthir » Fri Jul 31, 2015 6:33 pm UTC

Sizik wrote:
Xanthir wrote:Related: I'm kinda angry about any type system that can't overload based on return value, too. You have the information *right there* (or can type-infer it)! It's no more ambiguous than dispatching on any other argument! And it lets you write some very concise idiomatic code, like the From trait in Rust, where you can convert from anything to anything with `dest = from(source)`, as long as someone's defined a From::from() overload specialized on the dest and source types.


What if you call the method without assigning the return value to something?

I assume the language would have some sort of null type that it would type-infer as returning? So you'd have to actually write a just-the-side-effects-folks version that returns that null type.

I don't actually know how Rust does this, tho. I'll ask.

Edit: Ah, yup, if you don't declare a return type on your function, it's assumed to return the nil type, (). If you call a function as a statement, without assigning it to anything, it's also assumed to be returning into the nil type. So you do have to define a nil-returning overload if you want your function to be callable as a statement.

This is useful in normal circumstances, too - since Rust uses monads for exception handling, if your function can "throw" it actually returns an Either. A side-effects-only function that can throw would thus return an Either<(), SomeErrorType>.
Last edited by Xanthir on Fri Jul 31, 2015 6:41 pm UTC, edited 1 time in total.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))

User avatar
Thesh
Made to Fuck Dinosaurs
Posts: 6598
Joined: Tue Jan 12, 2010 1:55 am UTC
Location: Colorado

Re: Coding: Fleeting Thoughts

Postby Thesh » Fri Jul 31, 2015 6:41 pm UTC

ucim wrote:So, say I'm doing a project using OOP. I create an object and a method to do something, but that "something" is expensive. I need to do this thing for all such objects in a certain category; something satisfied by putting them in a loop:

(...)

How is this done optimally in OOP?

Jose


If your objects are dependent on the connection, they should maintain a reference to the connection. If your objects are not dependent on the database, but the method is, you should accept the connection as a parameter. Also, you should use [code] tags for readability when posting code.
Summum ius, summa iniuria.

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Coding: Fleeting Thoughts

Postby Xanthir » Fri Jul 31, 2015 6:44 pm UTC

And similarly, in functional programming you'd capture the db reference in a monad, so you could map all your operations through it, and when you pulled the result out it would make the expensive connection once, perform all the operations (which had been built up and delayed for this), then return the result.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))

User avatar
ucim
Posts: 6890
Joined: Fri Sep 28, 2012 3:23 pm UTC
Location: The One True Thread

Re: Coding: Fleeting Thoughts

Postby ucim » Fri Jul 31, 2015 7:30 pm UTC

The specific example I had in mind was writing to a database, where (in OOP) I'd write one object many times, but in procedural programming, I'd write many objects once. I tried to generalize it (badly - yes I do create the connection once and pass the result to all functions that need it, and maybe it's only my imagination that says that it's cheaper to write many objects once than to write one object many times.)

In general, if you need to do

Code: Select all

{ setup;
  do stuff;
  cleanup;
}


and setup and cleanup are expensive, it just seems to me that procedural programming lets you do

Code: Select all

{ setup;
  do lots of stuff;
  cleanup;
}

but OOP makes you do

Code: Select all

do lots of
{ setup;
  do stuff;
  cleanup;
}


because each instance of the object has to take care of itself. Am I wrongthinking?
Spoiler:
ok on the code tags; it's just that for small bits of code, I (personally) find code tag material harder to read.
Zanthir wrote:And similarly, in functional programming you'd capture the db reference in a monad...
Yeah, functional programming seems more like magic to me. I'm not at all used to the computer figuring out what to do given the answer I want!

Jose
Order of the Sillies, Honoris Causam - bestowed by charlie_grumbles on NP 859 * OTTscar winner: Wordsmith - bestowed by yappobiscuts and the OTT on NP 1832 * Ecclesiastical Calendar of the Order of the Holy Contradiction * Heartfelt thanks from addams and from me - you really made a difference.

User avatar
Flumble
Yes Man
Posts: 2264
Joined: Sun Aug 05, 2012 9:35 pm UTC

Re: Coding: Fleeting Thoughts

Postby Flumble » Fri Jul 31, 2015 9:37 pm UTC

The objects don't have to set up all their communications by themselves: they can all send their letters to a courier object that takes care of all the data in one go.

Xanthir wrote:And similarly, in functional programming you'd capture the db reference in a monad, so you could map all your operations through it, and when you pulled the result out it would make the expensive connection once, perform all the operations (which had been built up and delayed for this), then return the result.

I still don't fully get in what way you should put your pure functions (with arguments provided "via" the monad) inside a monad... and then "perform" the monad. Could you write (or link me to) an example doing this, for example logging calls to a function -assuming this is a use case for monads, which you can convince me it's not.


Forgot to mention: thanks MostlyHarmless for your explanation. I'll give it some more thought whether I'll try to teach it to the person in question or not. It's working now (which is the intent of the exercise) with a for-loop instead of a higher-order map, which means endless freedom in types, but the whole set-up could benefit from putting objects in cells instead of forcing them into vectors and matrices.

MostlyHarmless wrote:Keep in mind that cells are much slower than arrays whenever both are options. If you don't have to use a cell, then you shouldn't.

Luckily, it's mostly an exercise in learning programming (in matlab, but I want to teach some core/functional concepts in return for helping :roll: ) than efficiency, so that's not a hold-back.

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Coding: Fleeting Thoughts

Postby Xanthir » Sat Aug 01, 2015 12:13 am UTC

Flumble wrote:The objects don't have to set up all their communications by themselves: they can all send their letters to a courier object that takes care of all the data in one go.

Right. More explicitly: whatever variables are being altered by the imperative setup() and cleanup() calls, you just put into an object. Call setup() on that object once, then inside the loop pass it to all of the objects being made, so they can take advantage of it, then after the loop, call cleanup() on it. Done.

Xanthir wrote:And similarly, in functional programming you'd capture the db reference in a monad, so you could map all your operations through it, and when you pulled the result out it would make the expensive connection once, perform all the operations (which had been built up and delayed for this), then return the result.

I still don't fully get in what way you should put your pure functions (with arguments provided "via" the monad) inside a monad... and then "perform" the monad. Could you write (or link me to) an example doing this, for example logging calls to a function -assuming this is a use case for monads, which you can convince me it's not.

That's general IO monad stuff, which I just finally grokked. The IO monad is really the "here's a bunch of functions, run them eventually when I ask you to, along with whatever expensive/side-effecty stuff you were created for" monad. You give it the "bunch of functions" by mapping them over it.

Here's a simpler example of the IO monad, in Javascript:

Code: Select all

var file = readFile("foo.txt");
// readFile returns an IO<String>
// It hasn't *actually* read the file yet, but we can pretend it has.
// Right now it actually just holds a function that'll read the file and return a string.
var firstLetter = map(function(text){return text[0];}, file);
// When we map the function over the IO, it really just makes a new IO,
// with the original stored function and our newly mapped one.
var result = chain(function(text){writeFile("bar.txt", text);}, firstLetter);
// writeFile also returns an IO. We don't want nested IOs,
// so we use chain() instead of map() to map it over the firstLetter IO.
// That "flattens" the IOs, again storing the actual writing function away in the result IO.
// Again, it hasn't actually written the file yet.
result.unsafePerformIO();
// This finally triggers the IO.
// It runs all the stored functions - first it retrieves the file, then it grabs just the file's first letter, then it writes the file.


This is how functional programming does side-effects and global state - it delays actually executing the side-effect code, just pretends that it's run and manipulates the values that it'll eventually have, and then at some point pulls the trigger and says "fuck it, time for side-effects". This lets the rest of the program before this final point work in a pure fashion; you can safely call any function and know it won't do anything other than return a value.

(This is assuming appropriate definitions of map(), chain(), and the read/write functions, of course.)
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))

User avatar
ucim
Posts: 6890
Joined: Fri Sep 28, 2012 3:23 pm UTC
Location: The One True Thread

Re: Coding: Fleeting Thoughts

Postby ucim » Sat Aug 01, 2015 4:00 am UTC

Flumble wrote:The objects don't have to set up all their communications by themselves: they can all send their letters to a courier object that takes care of all the data in one go.
Brilliant! I think...

So now, instead of

Code: Select all

do lots of
{ object.do-stuff()
}
//where
object.do-stuff // is
{ setup; // expensive
  do-this;
  cleanup;
}
I would

Code: Select all

do lots of
{ object.mail-stuff();
}
courier.engage();

//where
object.mail-stuff() // is
{ send.to.courier($this);
}
//and object: courier is a list of the data of the objects...
// and
courier.gatherup($object) // is
{ for each piece of important data of $object
  {  add to $list;
  }
}
// and
couier.engage() // is
{ setup; // still expensive
  for each item in $list
  { do-this;
  }
}


Somehow (and this is what turned me off of OOP in the first place) this looks a lot more complicated than just the procedural method. The stuff getting done is the same, but it is less straightforward, because of all the communicating being done.

Or am I doing it wrong?

Jose
Order of the Sillies, Honoris Causam - bestowed by charlie_grumbles on NP 859 * OTTscar winner: Wordsmith - bestowed by yappobiscuts and the OTT on NP 1832 * Ecclesiastical Calendar of the Order of the Holy Contradiction * Heartfelt thanks from addams and from me - you really made a difference.

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Coding: Fleeting Thoughts

Postby Xanthir » Sat Aug 01, 2015 5:24 am UTC

Well, the communicating is always done, but now it's explicit. Rather than a function which has mysterious and unpredictable side-effects on arbitrary shared global state, you have some more-easily-followed code that is explicit in what it's doing and has a tightly scoped effect. Nothing happens outside of the code you're looking at.

Of course, you can still probably write it better. For example, Python's `with` statement makes this pattern really easy to manage:

Code: Select all

with someSetup() as s:
  for thing in someThings:
    doStuff(thing, s)


Where someSetup() creates an object with __enter__() and __exit__() methods which are automatically called when you enter or leave the block. This is actually *smaller* than your original code and less error-prone, because there's no way to forget to call the cleanup code. (A surprisingly common error in this kind of code.)

Note another important benefit of this kind of thing - error handling is handled properly! No need to worry about what to do if something in the loop throws; in your procedural code, you have to remember to catch it and do cleanup.

All in all, procedural code can look a little simpler, but only because it offloads a lot of the complexity to the programmer's mind. Better programming paradigms make the complexity explicit; good languages let you still hide that complexity behind convenient syntax, so you don't even pay a syntax tax.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))

User avatar
Flumble
Yes Man
Posts: 2264
Joined: Sun Aug 05, 2012 9:35 pm UTC

Re: Coding: Fleeting Thoughts

Postby Flumble » Sat Aug 01, 2015 7:28 am UTC

So my renewed understanding (from DrBoolean and Xanthir) is that
The readFile function reads a file and returns the contents of the file as a string.

is a lie. It just gives a monad to which you can attach even more lies post-poned functions and nothing is done until some function explicitly needs a result of readFile.

The monadicity of List now eludes me, except for the part where you can exploit the syntactic sugar to perform a function for every element.

Xanthir wrote:

Code: Select all

with someSetup() as s:
  for thing in someThings:
    doStuff(thing, s)

...and in the specific case of a connection, using a constructor (as the expression) to complete the OOPness:

Code: Select all

with Connection(to, some, location) as connection:
  for obj in whateverSetYouHaveOfObjectsThatMightNeedSomethingSent:
    obj.send(connection)

(sorry for the identifier "obj" -in my defence, there's not enough information to give it a more descriptive name)

User avatar
Jplus
Posts: 1721
Joined: Wed Apr 21, 2010 12:29 pm UTC
Location: Netherlands

Re: Coding: Fleeting Thoughts

Postby Jplus » Sat Aug 01, 2015 11:07 am UTC

Yakk wrote:I have lots of problems with C++.

For example, virtual functions and inheritance and virtual inheritance. What they did was pick one *particular* way to implement those concepts, and fix them in the language.

If you want to pick a *different* way to do dynamic dispatch, the language gives you next to no support.

[...]

I never thought about this. I agree! If you're using pointers to enable semi-dynamic typing anyway, why not make the mechanism itself accessible at runtime?

ucim wrote:So now, instead of

Code: Select all

do lots of
{ object.do-stuff()
}
//where
object.do-stuff // is
{ setup; // expensive
  do-this;
  cleanup;
}
I would

Code: Select all

do lots of
{ object.mail-stuff();
}
courier.engage();

//where
object.mail-stuff() // is
{ send.to.courier($this);
}
//and object: courier is a list of the data of the objects...
// and
courier.gatherup($object) // is
{ for each piece of important data of $object
  {  add to $list;
  }
}
// and
couier.engage() // is
{ setup; // still expensive
  for each item in $list
  { do-this;
  }
}


Somehow (and this is what turned me off of OOP in the first place) this looks a lot more complicated than just the procedural method. The stuff getting done is the same, but it is less straightforward, because of all the communicating being done.

I think you are mixing in the monadic-like stuff now, too. As Xanthir and Flumble already pointed out, this can be much simpler. Here's the pattern in your own notation:

Code: Select all

do once
{   courier.setup();
    do lots of
    {   object.do-this-with(courier);
    }
    courier.cleanup();
}

// where
object.do-this-with(courier) // might be
{   prepare my-data;
    courier.send(my-data);
}

The take home lesson is that OOP and procedural programming are not mutually exclusive. In fact they combine very well. There's nothing in OOP that requires you to coerce the entire setup-send-cleanup cycle in a single call to a single object.
"There are only two hard problems in computer science: cache coherence, naming things, and off-by-one errors." (Phil Karlton and Leon Bambrick)

coding and xkcd combined

(Julian/Julian's)

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Coding: Fleeting Thoughts

Postby Xanthir » Sat Aug 01, 2015 6:46 pm UTC

Flumble wrote:So my renewed understanding (from DrBoolean and Xanthir) is that
The readFile function reads a file and returns the contents of the file as a string.

is a lie. It just gives a monad to which you can attach even more lies post-poned functions and nothing is done until some function explicitly needs a result of readFile.

The monadicity of List now eludes me, except for the part where you can exploit the syntactic sugar to perform a function for every element.

That's exactly what monadicity means!

A monad is just a slightly powered-up functor. A functor is just a stupid name for a container that holds some value, which you can map() over. That's it - if you hold a value, and can map, you're a functor. The extra power of a monad is a join() method, which takes a *double-wrapped* value and smooshes the two wrappers together into one, with some reasonable restrictions on what "smooshed together" means. This is useful when you've got a really useful functor, such that a lot of methods want to return values wrapped in it. (Like both readFile and writeFile, both returning an IO monad.) If you've got no way to smoosh wrappers together, each time you map() you're just stacking another layer of wrapper on the value, and it gets *really* unwieldy; monads help you keep it simple.

So an IO is a container that holds a side-effecty function, which it'll run later. When you map, it holds onto your function, and runs it *eventually* on the result of the side-effecty function, when you ask for the value. When you join() it just concats the lists of functions that the two IOs have built up and returns you a new IO with the combined list.

An Array is a container that holds zero or more values. When you map, it applies your function to each value, giving you a new Array holding the return values. When you join() (called on an Array of Arrays of values) if flattens the arrays by one level (or, if you prefer, concats all the child arrays and returns the result, same thing).

A Maybe is a container that might hold a value, or might not. When you map, if it holds a value, it applies it and gives you a new Maybe holding the result; if it doesn't, it gives you another empty Maybe without running your function. When you join() (called on a Maybe possible holding a Maybe possibly holding a value), if both Maybes are value-ful it strips one of them off; if either is empty it gives you another empty Maybe.

And so on. Every functor is just a container with map() defined for it. Every monad is just a functor with join() defined for it (and of(), which puts a value into a "default" version of your container; this is called "return" in Haskell). It's *stupidly* simple, but surprisingly fertile; tons of concepts can be boiled down to these structures, and you can do tons of useful things knowing nothing more than "this is something with map() and join()".

(Note that there's no common way of *extracting* a value from a functor/monad. The contract doesn't require one, and that's good, because it allows a greater diversity of concepts to be represented using these patterns. So each functor/monad defines its own extraction method that you have to learn. (There's another pattern called "comonad" that captures the idea of "a common way to extract a value from a container" and "extending a container". The (unfold) function in my sig is actually exploiting the fact that lists are comonads, too.))

Because of how they're commonly used, monads are *usually* operated on through chain() (called "bind" or ">>=" in Haskell). This is just a map() followed by a join(), composed together for convenience (and possibly given a more efficient implementation).

And that's all monads are. A stupidly simple algebraic structure, that happens to be super useful for a ton of different abstractions in programs. All the complexity of monads is in the tricky ways you can model things as monads, and all the weird syntax sugar people build up on top of them. And, for some reason I cannot fathom, no one can explain this in a reasonable way. I think they're all broken from learning monads via Haskell's syntax sugar. It took me a lot of puzzling through Typeclassopedia to get halfway there, and DrBoolean's guide finally sent me over the edge into complete understanding.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))

User avatar
Xeio
Friends, Faidites, Countrymen
Posts: 5101
Joined: Wed Jul 25, 2007 11:12 am UTC
Location: C:\Users\Xeio\
Contact:

Re: Coding: Fleeting Thoughts

Postby Xeio » Tue Aug 04, 2015 2:54 pm UTC

Make method private. Everything compiles. Something stops working.

Try making it public again, things start working again.

Eugh, why would someone be reflecting over this...

korona
Posts: 495
Joined: Sun Jul 04, 2010 8:40 pm UTC

Re: Coding: Fleeting Thoughts

Postby korona » Wed Aug 05, 2015 6:21 pm UTC

Yakk wrote:If you want to pick a *different* way to do dynamic dispatch, the language gives you next to no support.

You could try to build a more powerful dynamic dispatch mechanism via some template metaprogramming :D.

User avatar
Yakk
Poster with most posts but no title.
Posts: 11129
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Coding: Fleeting Thoughts

Postby Yakk » Thu Aug 06, 2015 1:09 am UTC

Yes, but the syntax is ugly.

You could phoenix/hana it up, I suppose. Use operators like + to add new methods to a type, have the result of the constexpr expression be of a type of the type of the resulting type, etc.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Coding: Fleeting Thoughts

Postby Xanthir » Fri Aug 07, 2015 6:33 pm UTC

Ah, I finally figured out the Reader monad. My mistake was thinking that it was usable in the normal way that monads are, via independent bind calls, but it's not. In order to get any use out of it, you *must* do nested binds, or more reasonably, you must use do notation.

Reader and Writer are always presented together, I assume so you can then segue straight into the State monad that kinda-sorta does both of them together (but not really), but the two are actually *completely* different things which work in 100% unrelated ways. I hate Haskell tutorials. >_<

Related: HOLY SHIT I HATE HASKELL TUTORIALS. In a "basics" tutorial for the Reader monad, someone actually thought that this was a reasonable, instructive line to write:

Code: Select all

greetAndSeeYou = liftM seeYou nameReturn >>= putStrLn


This example uses two "threading" functions (functions that modify the way data flows through other functions) - liftM and >>=.

Unforgivable Sin 1: It uses one in function form, and the other in operator form.
Unforgivable Sin 2: Despite the two being basically identical (one maps across a functor, the other maps across a monad), they take their arguments in opposite order.
Unforgivable Sin 3: As a result of this, the actual data flow is "call nameReturn, map seeYou over the result, map putStrLn over the result". MIDDLE-ENDIAN DATAFLOW, IN A FUCKING BASIC TUTORIAL. WHAT.
Unforgivable Sin 4: No parens used, so a beginner has to have already intuited the way Haskell function calls work, and the arg count of each function involved, to understand that it should be organized as "(liftM seeYou nameReturn) >>= putStrLn".

I mean, Haskell syntax is fucked to begin with. It got its core concepts frozen back before we realized how to organize them properly, so everything's ass-backwards and inconsistent. A slightly more reasonable way to write this would be:

Code: Select all

greetAndSeeYou = putStrLn =<< (seeYou `liftM` nameReturn)


This at least uses the threading functions (a) in the same form (both operators) and (b) in the same argument order (LHS function, RHS value that function will be mapped over). This is the more logical order, too, as it makes the threading function into an analog of normal function application - you're calling the function with an arg, but the arg is actually in a wrapper and this automagically gets handled for you. It's how <$> and <*> work already for Applicatives! WHY DO THE THREE CLOSELY-RELATED CORE CONCEPTS IN HASKELL HAVE THREE COMPLETELY DIFFERENT USE CONVENTIONS?!?

Functor: function syntax, takes function arg and then functor arg
Applicative: operator syntax, takes function arg and then applicative arg
Monad: operator syntax, takes monad arg and then function arg

I love FP. I hate Haskell. Argh.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))

Ubik
Posts: 1016
Joined: Thu Oct 18, 2007 3:43 pm UTC

Re: Coding: Fleeting Thoughts

Postby Ubik » Fri Aug 07, 2015 6:49 pm UTC

It's just an excuse, but now I feel bit relieved that I haven't yet got much into Haskell. (Though I feel I should learn some purely functional language, or actually, really get the common main concepts in them.)

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Coding: Fleeting Thoughts

Postby Xanthir » Fri Aug 07, 2015 6:59 pm UTC

Go read my DrBoolean thread (just a thread or two down the page). He actually gives a shit about teachability.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))

Ubik
Posts: 1016
Joined: Thu Oct 18, 2007 3:43 pm UTC

Re: Coding: Fleeting Thoughts

Postby Ubik » Fri Aug 07, 2015 7:27 pm UTC

That seems interesting, I think I will check it out. Not having to learn completely different looking language at the same time as working with the concepts seems like a good idea. Thanks!

User avatar
Flumble
Yes Man
Posts: 2264
Joined: Sun Aug 05, 2012 9:35 pm UTC

Re: Coding: Fleeting Thoughts

Postby Flumble » Fri Aug 07, 2015 9:51 pm UTC

Xanthir wrote:Functor: function syntax, takes function arg and then functor arg
Applicative: operator syntax, takes function arg and then applicative arg
Monad: operator syntax, takes monad arg and then function arg

I hadn't even noticed the syntax inconsistency, but I was already really bummed by the fact that you've got fmap, liftA, <$> and liftM which are the same by definition (learn you a haskell still claims they're different, but it's dated)

I also don't get how (->) is a functor-applicative-monad in some Haskell source. It's a keyword! Using it anywhere but in type constructors results in parse errors!

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Coding: Fleeting Thoughts

Postby Xanthir » Fri Aug 07, 2015 10:20 pm UTC

I don't know Haskell syntax well enough to comment on the use of an arrow as syntax, but a partially-typed function (one with its input type already specified) is definitely a monad. Functions and lists/dicts are semantically equivalent.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))

User avatar
ahammel
My Little Cabbage
Posts: 2135
Joined: Mon Jan 30, 2012 12:46 am UTC
Location: Vancouver BC
Contact:

Re: Coding: Fleeting Thoughts

Postby ahammel » Sat Aug 08, 2015 3:22 am UTC

Flumble wrote:
Xanthir wrote:Functor: function syntax, takes function arg and then functor arg
Applicative: operator syntax, takes function arg and then applicative arg
Monad: operator syntax, takes monad arg and then function arg

I hadn't even noticed the syntax inconsistency, but I was already really bummed by the fact that you've got fmap, liftA, <$> and liftM which are the same by definition (learn you a haskell still claims they're different, but it's dated)

Our style guide at work enforces <$> in most circumstances. It's acceptable to use fmap in point-free mode.

Code: Select all

-- fine
f x = g <$> h x
-- also fine
f = fmap g . h
-- less fine
f x = fmap g $ h x
-- listen: I hate you
f x = g `liftM` h x


Most of Control.Applicative is in Prelude as of GHC 7.10 (which, sadly, we haven't moved to yet), which makes that easier.

Haskell probably has too much syntax.
He/Him/His/Alex
God damn these electric sex pants!

User avatar
cemper93
Posts: 209
Joined: Sun Feb 20, 2011 2:35 pm UTC
Location: `pwd`

Re: Coding: Fleeting Thoughts

Postby cemper93 » Sat Aug 08, 2015 8:31 am UTC

Xanthir wrote:Because of how they're commonly used, monads are *usually* operated on through chain() (called "bind" or ">>=" in Haskell). This is just a map() followed by a join(), composed together for convenience (and possibly given a more efficient implementation).

Nitpick: the most common monad operations can indeed be implemented in terms of each other, but Haskell's definition doesn't base bind on join and map, but bases everything else on return and bind. (This is also the most succinct definition for an instance of Monad -- you always need to have an explicit definition for return and at least one other operation.)

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Coding: Fleeting Thoughts

Postby Xanthir » Sat Aug 08, 2015 11:11 pm UTC

Right, that's just an implementation detail of Haskell. I was talking in general terms, where explaining monads as "functors with join()" is *way* better for teaching, and then chain/bind as an optimization for the common case of "map followed by join".
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))

User avatar
ahammel
My Little Cabbage
Posts: 2135
Joined: Mon Jan 30, 2012 12:46 am UTC
Location: Vancouver BC
Contact:

Re: Coding: Fleeting Thoughts

Postby ahammel » Sun Aug 09, 2015 4:19 am UTC

Speaking of too much syntax, I've just spent entirely too long writing in an imaginary
Lisp-flavoured Haskell dialect and I'm starting to seriously consider implementing the damn thing. Send help.

Code: Select all

(::  main (-> (IO Nil)))
(def main
  (do (<- (greeting (getLine))
          (name     (getLine)))
    (putStrLn (concat '(greeting ", " name "!")))))

(::  map (-> (-> a b) (List a) (List b)))
(def map (_ '()) '())
(def map (f (list x . xs)) (cons (f x) (map f xs)))

(::  const (-> a b a))
(def const (a) (lambda (_) a))

(::  flip (-> (-> a b c) b c a))
(def flip (f x y) f y x)

(defclass (Functor f)
  (:: fmap (-> (-> a b) (f a) (f b))))

(defclass (=> (Functor f)) (Applicative f)
  (:: pure (-> a (f a)))
  (:: ap   (-> (f (-> a b)) (f a) (f b))))

(defclass (=> (Applicative m)) (Monad m)
  (::  return (-> a (m a)))
  (def return pure)
  (:: >>= (-> (m a) (-> a (m b)) (m b)))
  (::  >> (-> (m a) (m b) (m b)))
  (def >> (m n) (>>= m (const n))))

(::  =<< (=> (Monad m)) (-> (-> a (m b)) (m a) (m b)))
(def =<< (flip >>=))

(::  join (=> Monad m) (-> (m (m a)) (m a)))
(def join (=<< id))

(deftype (Maybe a)
  (Nothing)
  (Just a))

(definstance Functor (Maybe a)
  (def fmap (_ Nothing) Nothing)
  (def fmap (f (Just a)) (Just (f a))))

(definstance Applicative (Maybe a)
  (def pure Just)
  (def ap (Nothing  _)        Nothing)
  (def ap (_        Nothing)  Nothing)
  (def ap ((Just f) (Just a)) (Just (f a)))

(definstance Monad (Maybe a)
  (def >>= (Nothing  _) Nothing)
  (def >>= ((Just a) f) (f a)))
He/Him/His/Alex
God damn these electric sex pants!

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Coding: Fleeting Thoughts

Postby Xanthir » Sun Aug 09, 2015 5:59 am UTC

I'd write in it. We can get the monad hierarchy correct. ^_^
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Coding: Fleeting Thoughts

Postby Xanthir » Sun Aug 09, 2015 8:28 am UTC

I realized I still didn't have a 100% grasp on Reader, since I couldn't explain what ask or local did, so I started by writing Reader in JS in terms of map() and join() (writing bind() directly just means doing map() and join() together; writing them separately lets you figure out the individual bits by themselves), and then with that understanding, writing ask/local/asks.

Code: Select all

function Reader(f) {
  // Reader "wraps" a value, but in a roundabout way.
  // Rather than containing a value directly, like Maybe does,
  // it contains a function that'll eventually compute the value,
  // when you finally provide it with an environment to read from.
  this.fromEnv = f;
  // Using fromEnv instead of runReader
}
Reader.prototype.map = function(f) {
  // takes a function from a->b
  // Because Reader doesn't contain its value yet,
  // you have to make *another* delayed function that'll run the map eventually,
  // when you're finally given the dang environment.
  return new Reader(compose(f, this.fromEnv));
  return new Reader(env => {
    let a = this.fromEnv(env);
    let b = f(a);
    return b;
  });
}
Reader.prototype.join = function() {
  // I have a Reader which, when given an environment,
  // will return another Reader, which can be given the environment to compute the real value.
  return new Reader(env => this.fromEnv(env).fromEnv(env));
  return new Reader(env => {
    var inner = this.fromEnv(env);
    var val = inner.fromEnv(env);
    return val;
  });
}
Reader.of = function(val) {
  // I don't give a shit about the environment you'll pass to me.
  return new Reader(()=>val);
}
Reader.env = function() {
  // Get the environment so you can work with it too,
  // by making a Reader with the environment as it's eventual value.
  // Reader.env().map(f) is identical to new Reader(f),
  // but nice because you can jump straight into the monad without worrying.
  return new Reader(id);
  return new Reader(env => env);
}
// Using env instead of ask
Reader.changeEnv = function(trans, r) {
  // Transforms the environment that the reader R will see to a new local environment
  // (the env it would have received is passed through "trans" first).
  return new Reader(env => r.fromEnv(trans(env)));
}
// Using changeEnv instead of local
Reader.asks = function(f) {
  return new Reader(f);
  return Reader.env().map(f);
}


Now I'm angry again that nobody knows how to write a tutorial worth a damn. The "asks" function is written in a complicated fashion as "asks sel = ask >>= return . sel". If you follow the definitions, you find that this is identical to the Reader constructor - "asks f" and "Reader f" return the same thing - except that this definition allows it to work for any "readerish" monad (any that declare themselves as instances of the MonadReader typeclass). This fact was never mentioned in anything I've read so far, leaving me to puzzle out a relatively roundabout definition and its purpose on my own. >_<

Edit: And I'm mad again now that I look at it and realize this should have been written "asks sel = fmap sel ask", but their typesystem is still broken and monads aren't automatically functors, so you have to use too-powerful abstractions for simple things and get silly code like that. (Or "asks = (<$> ask)" I guess, for the point-free points.)

Edit2: And with my new full understanding of Reader, State's just slightly more complex:

Spoiler:

Code: Select all

function State(f) {
  // Like Reader, it will *eventually* hold a value,
  // after you give it a state to work with.
  // For now, it holds a function that transforms a state
  // into a [value, new state] pair.
  this.fromState = f;
}
State.prototype.map = function(f) {
  // Original State takes an s1, returns an s2 and a value.
  // Just change it to return the mapped value instead.
  return new State(s1 => {
    let [a,s2] = this.fromState(s1);
    return [f(a), s2];
  });
}
State.prototype.join = function() {
  // Outer State takes an s1, returns an inner State and an s2.
  // Pass the s2 to the inner State, get a value and an s3.
  // Joined State thus takes an s1 and returns the value and an s3.
  return new State(s1 => {
    let [m, s2] = this.fromState(s1);
    let [a, s3] = m.fromState(s2);
    return [a, s3];
  });
}
State.of = function(a) {
  return new State(s1 => [a, s1]);
}
State.get = function() {
  // Expose the state by giving it as the value, too.
  return new State(s1 => [s1, s1]);
}
State.gets = function(sel) {
  // Expose a subset of the state via the selector function
  return new State(s1 => [sel(s1), s1];
}
State.put = function(s) {
  // Set the state, ignoring the old value and state.
  return new State(s1 => [null, s]);
}
State.modify = function(f) {
  // Transform the old state into a new state, ignoring the old value.
  return new State(s1 => [null, f(s1)]);
}
State.prototype.eval = function(s) {
  // Get the value out as a plain value.
  let [a,_] = this.fromState(s);
  return a;
}
State.prototype.exec = function(s) {
  // Get the state out as a plain value.
  let [_,s1] = this.fromState(s);
  return s1;
}
State.prototype.mapBoth = function(f) {
  // Apply a function to both the value and the state?
  return new State(s1 => {
    let [a,s2] = this.fromState(s1);
    return [f(a), f(s2)];
  });
}
State.changeState = function(trans, m) {
  // Like Reader.changeEnv, modifies the state that m will eventually see by passing it through trans first.
  return new State(s => m.fromState(trans(s)));
}
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 7 guests