đź““ Distributed library exercises
How about: "Distributed programming with Unison: tutorial and exercises"
đź““ Distributed library exercises
How about: "Distributed programming with Unison: tutorial and exercises"
And imagine if you could create multiple services in this way, all of which can trivially talk to each other, without a layer of cruft. Wouldn't that be awesome?
Testing!
You can call functions without any ceremony or boilerplate (no need to convert all the arguments to JSON blobs and back, for instance), introduce new functions and data types as you see fit, and a friendly typechecker is ever by your side to help ensure the programs you're assembling actually make some sense.
I like what you're going for in this sentence but it reads a bit awkwardly.
(micro)services
I'd probably just pick either "microservices" or "services" and go with that throughout the whole article.
zipWith is a function which takes in two lists and a function which operates on the elements in both lists sequentially until the end of one of the lists is reached.
Maybe show a quick example of its usage in case users are unfamiliar? Or is it sufficient that zipWith will hyperlink to the definition which will have docs?
Maybe link to zipWith.doc directly if you don't want them to see the source cuz that would give away the answer.
We've seen a couple of built-in types already, but here's a basic vocabulary of Unison types to get you started writing Unison programs.
0xs934820320498234098234 is a value of type Bytes
0xff is of type Nat
foldb is a function which operates on a list of elements as, and applies a function f to each element the list, transforming it to another type. It then applies another function op to each successive element in the new list, with an argument z provided when it's the case that the list is empty.
I really have a hard time reading these prose descriptions of type signatures.
I might use foldLeft as the example? and then give several examples, some where a and b aren't the same type. For foldLeft you could just say foldLeft is a function that reduces a list to a single value, using a starting value and a 2-argument combining function.
Where should the parenthesis in the type signature go? Where should the implied parentheses go for this type signature?
Like this exercise. This is like one of those things that I've forgotten is confusing but it's great to test for understanding and hammer it home.
which takes in an a, and returns a function which then takes in a b, which returns a function that takes in a list of as to finally return a list of bs,
my brain exploded reading this sentence :)
I'd maybe just say "If we were to omit the parentheses from our signature, we'd end up with a rather useless function that that takes an a and a b rather than a function a -> b".
You could still give brokenMap but maybe just leave the RHS as = todo "???".
he only valid implementation of which is:
technically it could return the empty list too! this is really distracting me
lambdas
this term is used without defining it first
recommend making "anonymous function" a glossary term and the glossary entry can mention they're sometimes also called "lambdas" or "lambda functions"
Hello programming wayfarer,
Reading through this doc, it's very approachable and leisurely and I think this level of approachability is great for our base documentation... but maybe we can do something in this setup section to point FP gurus from Haskell and Scala to a separate document which is more of a whirlwind tour "Unison for Scala users" or "Unison for Haskell users".
A simplified view of the type signature of List.map is List.map where a and b are type variables.
I feel like I'm missing the type signature here that accompanies this text.
initialize between square brackets.
I'd just say "create using this syntax"
"initialize" is kind of imperative programming terminology IMO, where you'd create a thing and mutate it in place to "initialize" it. "initialize" -> "create"
But what if we need to sum up more slices. addSlices is a function which adds two numbers together to get a total.
It think it's actually possible to get away with not knowing much about currying to start. I might just say "To give a type signature for a function that takes multiple arguments, just put an -> between each argument, like so".
Then, after the example, you could mention that "Technically, Unison functions are "curried" which is handy if you want to call them with less than all their arguments. You can learn more about that here" and link to a separate module just on that?
I'm worried it's a bit too much to try to cover currying well in the middle of teaching basic stuff.
WDYT?
implementation like this:
do we need to explain the use syntax? I think it's probably clear from context, and also the UI will make the use keyword clickable so people can click through to get the full explanation of that syntax
slices.
slices?
like at1. They currently go up to at4.
I'd maybe just do say "like at1, at2, and so on and leave it at that, since at4 is not a fundamental limitation. We should just add up to like at20 to base and it's unlikely anyone will ever get that high anyway.
You cannot change the value once it's defined, so pick your favoritePie carefully. 🥧
I'm concerned this might raise more questions than it addresses here.
There's a separate notion of changing a definition which Unison does support - just redefining it in the codebase. Also Unison does have mutable references as well, but those are only available in the IO or STM ability.
Also this essay! the term 'variable' comes from mathematics (like in f(x) = sin(x) + 1, x is a variable). Variable is not a mutable reference.
https://existentialtype.wordpress.com/2012/02/01/words-matter/
I like the idea of meeting people where they're at but I also want to gently steer people in the right direction. Maybe we can talk about how best to do that.
but if you're the ambitious,
maybe "but even if you're the rule-breaking sort" (kill the "ambitious", seems a bit much)
more subdued would be "or, at the very least recommend you have UCM installed"
wdyt?
the summary section
guessing this will be a hyperlink in final version but just checking
We've
not sure on this tense. Maybe "We assume you've..."
the UCM
minor thing, we should decide if we're doing "the UCM", "UCM", or ucm and then use that consistently.
I think ucm as in "the executable program called ucm"
How about defining:
fragments.ucm = {{ [''ucm'']({glossary.ucm}) }}
glossary.ucm = {{
"UCM" stands for "Unison Codebase Manager". It's
the command line program you'll use to explore and
modify a Unison codebase and run Unison programs.
Go to [the docs site](https://unison-lang.org/docs/quickstart) for installation instructions.
}}
Then you can just do {{fragments.ucm}} in any paragraph where you are referencing that and it will get nicely linked back to the glossary entry. Also if we want to change our minds on this later we can do that easily.
I'd love it if any piece of terminology has a glossary entry like this which is linked at the point of use. Users are not necessarily going to be reading linearly through the docs and it's confusing to encounter terminology that isn't immediately explained in the vicinity of where you're reading.
The docs should be densely interlinked like this; think of wikipedia. And the doc viewer UI will have nice support for popping open (or even tooltip previewing) links above where you're reading.
Values
Minor thing - I'd call this section 'Defining terms'.
'Value' often has a more specific meaning that we might make use of when talking about abilities - it's something that's been evaluated. A term definition can have as its RHS a computation that may need to do some work before producing a value. Like if you say:
favoritePie = "Apple " ++ "Pie"
it won’t be long before we need our own protocol—analogous to git’s—to exchange the underlying objects efficiently, without the extra layer of versioning.
I could imagine just always doing a force push to git, or maybe doing a force push periodically. Basically git just gets used as a syncing protocol, not as a way of recording history, since the repo format already has history baked in.
Maybe (Causal m h e)
Question: why Maybe here? Doesn't the Map already give you maybe-ness?
Either e (m e)
I'd probably just make this m e, it can always be formed via pure and it will make consuming code more uniform.
Stream.fold : (b ->{g} a ->{g} b) -> b -> '{g, Stream a} r ->{g} b (+2 metadata)
I really like what you did here. A+
Stream.Int.all : '{Stream Int} a (+2 metadata)
Test annotation: what's the order that these are generated in?
cachedRead :: (MonadFix m, MonadIO m) => Deserialize m h e -> m (RawHash h -> m (Causal m h e))
Testing out this annotation stuff.