-
Notifications
You must be signed in to change notification settings - Fork 438
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Curriculum Review by @brianloveswords #7
Comments
ALSO, forgot to make it clear – I'm totally willing to make some PRs for this stuff. I wanted to review first to get your opinion before I jumped in and started making changes! Let me know which things you think are worth adding/modifying and I'll get on it. |
Wow thanks for this feedback, exactly what I was looking for! Some of the points you've raised are listed in here: #2 magical $injectionInjection is horribly magical, but is just as magical as process.argv[n] for someone who doesn't use node, and the tutorial is aimed at non-noders. I'm thinking the export a function and passing in the arguments would work pretty well and wouldn't require too much noise, and reduces the magic. I'll refactor. Meta InfoI relied a bit on being able to physically talk people through each lesson. Definitely needs more explanation about what the purpose of each lesson is and some more detail about how to work the functions. Understanding how reduce worked at all was particularly difficult for many. Repeat SolutionAgreed, the recursive solution is better. Perhaps an introduction to recursion should occur prior, or perhaps this could be 'higher order functions & recursion' with some more introductory text. MapThe point here was to give a minimal example to help people connect the for-loops + accumulator array pattern with maps… As in, "any time you see this pattern, it's a good case for using map". People are very attached to their for-loops, even after I told them not to use them, people kept writing them! I'm thinking I need to use esprima to parse their code and fail on any for-loops. I'm happy to change the exercise to operating over an array of objects, as that's probably more real-worldy. FilterI have a feeling it wouldn't be hard to implement fences, even if it just changed the colour and indented it. ReduceYeah this one was a bit tricky on multiple fronts. Everyone stumbled on the "extra credit" and I've removed it and added a simpler reduce example (available on master branch). And I'll introduce apply/call/arity on their own in a prior exercise. Partial Application without BindYep, the arguments -> real array thing needs explaining. Sounds like it could fit well into the apply/call/arity exercise. SpyI find myself using this pattern for monkey patching bad behaviour of other libraries. Good practice? probably not. But it's easier/safer than running/depending on my own fork. Capitalisation is a pattern I'm seeing recently (e.g. MuxDemux examples) where the capitalisation simply means, 'this is a factory', rather than 'this is a constructor', which I guess is a specific type of factory. ConclusionThanks a lot for your detailed feedback! |
Also tidied some problem.txts lacking info. Closes #9 as suggested by @brianloveswords in #7.
I was going to send this as an email until I found this discussion: Firstly can I to take the opportunity say how much I have enjoyed working through (most) of your node school module. If I have appeared critical on the forum I apologise. Some of the exercises I have found frustrating but mostly at my own inability to work out the solution. I know you have extended an extraordinary amount of time and effort in putting this module together and for that I offer a big thank-you. I have learnt a lot of tips and techniques so far and, looking at your solutions, which are always neat, concise and very elegant, your knowledge of JavaScript is clearly at a level that John Resig terms Ninja Master. I am certainly trying to emulate your style in my own coding. I also suspect, from the way the exercises have jumped forward, that you're one of those people that can sketch out the solution to a complex maths problem in 3 lines whereas it would take someone like me about 10. That said, I would like to offer a few suggestions for the module. Higher Order FunctionsHaving worked through all of the The second exercise, a) Basic: Every SomeI wonder whether the introduction to this module might not be changed to something like:
b) Partial Application without Bind / Partial Application with BindThen, when we get to the later lessons of Partial Application without Bind and Partial Application with Bind the current instructions of "Your implementation should take a namespace string, and return a function that prints
Also, I found the implementation a little confusing in that I was expecting first: a namespace, then second: a series of sentence strings. Instead it appears that the implementation is a single namespace then a series of words that need to be concatenated to form the sentence. under Arguments I feel this could described as:
A similar description for the implementation and second input could be applied to Partial Application with Bind. Basic: ReduceI personally found the number of new concepts introduced in this exercise to be a bit overwhelming. The previous lesson, I wonder whether an intermediate exercise might not assist the student in gaining insight into iteration and caching. Say, Then, once the student understands the concepts of caching and iteration, introduce
The student may not be familiar with how
Partial with BindI personally struggled to get my head around apply(), call() and, in particular bind(). As with
The other difficulty I found in analysing your solution was the use of the Console object. Returning Partial without BindFollowing on from above. Your solution to this exercise makes use of the "Let’s say you’re passing a callback to someone else’s library and you’re expecting data back, but you don’t know how many arguments you’re going to get. There’s a simple trick to allow you to console.log everything that’s passed to your callback.
"Because apply takes an array of arguments to be passed to the function, you can sling it the array of arguments you received and have them logged out, all nicely formatted." The use of I hope this has been of some use. I still have 7 more exercises to complete and will report back at the end. |
Hi @timoxley - thanks for an awesome workshop. In the meantime, just wanted to put in my responses to the comments above. Hello Worldloving the way you've now set up each exercise to export a function. Much prefer this to the process.argv[n] option, as I think it is closer to what I'm dealing with on a daily basis. I'm a slightly more experienced coder, so I found the first few exercises fairly easy, the middle ones harder, and haven't finished the last ones yet. Mapagree that the solution could also look like this:
could you show both options in the solution, so people can see that they are equivalent, and just use different styles? Tim - you mention "any time you see this pattern, it's a good case for using map" here in this issue - put that into the exercise itself too - either in the problem or solution text. FilterAgain, I like seperating out the functions rather than using Partial application without bindI also really struggled to concatenate a string onto the front of an array properly - took me ages and loads of trial and error. I also think it might be nice to put this stuff into a separate exercise. ConclusionTim you've asked @brianloveswords to add in the meta descriptions, but he hasn't put them in yet has he? Shall we go ahead and do this? Lastly@timoxley what are your thoughts about putting in any of the changes that @magwitch has suggested? I agree that the pace of learning is fast, often with multiple concepts to be understood between exercises. Would just be good to hear your response to their comments. Ps.I seem to have ground to a halt around exercises "Implement Map with Reduce" and "function spies", and I can't see any other users comments or issues around the later exercises. Are a lot of people dropping out of the workshop before they reach the end, do you know? Is there any way we can track the point at which people drop out, or stop making progress? Pps.How do you feel about using ascii art for when you complete a workshop? https://github.com/eiriksm/workshopper-hooray |
Hello World
Before starting...
program that looks at
process.argv
?doing this. I don't yet understand what this has to do with
functional javascript. NOTE that it doesn't necessarily have to
do with functional js, but some text saying "hey, this is just to
get your feet wet, make sure you got the basics, etc" would be great!
During...
process.argv[2]
. I'm getting back a string, butthat string is wrapped in quotes. That's weird, but maybe okay?
string, but verify wants it without the quotes.
.replace(/"/g, '')
the string, even though Iknow that's not not correct, just so I can see what the solution is.
After finishing...
$input
is a global that gets injected into thescript. That is weird to me – I think it'd be better if either
A) it used (and expected) process.argv[2] OR
B) it expected a module with exactly one function that does what is expected.
functional node.js. So maybe the global injection is the right
move? Not sure...
Higher Order Functions
Before starting...
theme. I don't really like that – it feels to magical, I like the
method used in
levelmeup
of building modules or taking thingson argv. This isn't a dealbreaker by any means, but I think it
does encourage better real-world practices.
After finishing...
Oh, I solved it totally differently:
I do feel like this might be slightly less "clever" – and it
teaches good recursion principles (always check your end
condition first, always make sure your input converges on the end
condition).
Basics: Map
Before starting...
The introduction could use a little more explanatory text as to
what a map is and why it's useful – something like the following:
"We often have to take a set of data and modify it before we
output it. For example, say we get this back from our database:
[{first: 'Brian', last: 'Brennan'}, {first: 'Alex', last:
'Sexton'}]
And we want a list of full names. We can do this with a for loop,
but we have to manually keep track of indices and push to a new
array on our own. A better way to handle this is by using a
map
. Amap
is a higher order function that operates eachelement of an array and returns a new array with each element
transformed
So to get our list of full names, our mapping function would be:
function fullName(item) { return item.first + ' ' + item.last }
And we'd call it like so:
var fullNames = entries.map(fullName)
Notice that the map function returns a new array – it doesn't
modify the array in place. This is a very important principle in
functional programming: it's always better to take input and
return new output rather than modify things in place.
Your exercise is to take an array of numbers and return a new
array with each number replaced by itself * 2..."
After finishing...
I think this might be cleaner and help illustrate the power of
passing function references around
I think the solution could also then explain the power of
creating small, generically useful functions and then using them
in maps to transform sets of data.
Basics: Filter
Before starting...
workshopper so that when the instructions are printed out the
things fenced in ```js are syntax highlighted? (That'd be a rad
module, and there's like a 90% chance substack has already
written it).
After finishing
Again, I like seperating out the functions rather than using
anonymous functions:
I think it better illustrates composition.
Basics: Reduce
Before starting...
Some illumination for why a reduce is useful would be
good. Perhaps even something that explains it in terms of mapping:
"A
reduce
(also known as a "fold left") is useful when you wantto take a bunch of values and turn it into just one. A good
example of this is taking the sum of a series:
The first argument to the
reduce
functor is known as theaccumulator
– this keeps track of a value as the functorprocesses values. The second argument to
.reduce
is known asthe seed value. This is useful for priming the
accumulator
sothe functor doesn't have to do conditional checking against it to
see whether or not it's the first value.
It's important to choose the correct seed value for the operation
you are performing! For example,
0
works well for addition, butif we wanted to get the factorial rather than the sum, we would
want to use
1
instead:(Fun fact: this has to do with 0 being the identity value for the
addition operation and 1 being the identity value for the
multiplication operation [citation needed])"
Woah, the boilerplate is confusing to me! I think to properly
explain
reduce
, the operation needs to be transparent – I thinkpart of doing
reduce
properly involves selecting the properseed
. That's an important lesson to teach, and one that getsobscured if the operation is opaque. It's also a lesson about
reduce
but the boilerplate function saysmap
.Array#map." – I think this is should be after a basic reduce
lesson. This is more advanced
reduce
, I think.After finishing...
lessons taught the optional arguments, and the lesson instructions
didn't say that we were supposed to implement
Array.prototype.map
exactly. The function signature in the"boilerplate" is also indicating that we should only implement
something with 2-arity.
expect people to understand
fn.call
andfn.apply
unless wemake a lesson about it earlier on.
Partial Application without Bind
Before starting...
first. Remind people about higher-order functions and tell them
that in this case, we want to make a function that takes input and
returns a new function.
hacking, that might be A) be a bad thing, or B) need to be
explained.
After finishing
Yeah, doing the arguments trick without explanation isn't
great. This should either be explained in an earlier lesson (along
with call and apply), or there should be a solution that doesn't
require it. I vote for the former. Here is my solution for
reference, though I think the published one is probably better
because
Array.prototype.concat
is rad and I don't use it nearlyenough.
Partial Application with Bind
Before starting...
After finishing
Function spies
Before starting...
explained upfront. Also that spies should probably only be used
for testing, debugging because the behavior is opaque to anyone
down the line. (I am very open to debate on this issue!)
After finishing
Ah, I was confused by the naming –
Spy
with an uppercase S – andI was expecting to build a constructor. I guess in a way it is?
I'm torn on this one. My solution was this:
Conclusion
This is awesome. I think with some interstitial lessons about
call
and
apply
, maybe one aboutreduce
before implementingmap
withreduce
, this can be a super rad workshopper! I'm definitely willingto help with creating/editing the intro text to add more
theory/reasons behind why someone would want to use FP techniques in
JS – I think that's important. Thank you so much for getting this
started!
The text was updated successfully, but these errors were encountered: