These are chat archives for got-lambda/expression

29th
Oct 2018
Jean-Louis Giordano
@Jell
Oct 29 2018 08:24
ok trying to catch up with that intense exchange from yesterday :p starting with a disclaimer: I'm super interested in types, I believe static analysis and static type safety is the future, I'm not here to say static types are useless (they are super useful). Given that, here is an example of what I believe Rich Hickey meant:
(defn credit-lookup [{:keys [ssn] :as person}]
  (assoc person :credit (when ssn {:income 2000 :age 42})))

(map credit-lookup
     [{:ssn "1234"}
      {:name "foo"}
      {:ssn "1234" :name "foo"}])
the credit-lookup function is open, in that it accepts any record or hash map, and will enrich that input with more tagged data
(you can use namespaced keys if you want to avoid collision)
now, that's probably not how you would approach a problem in Haskell or purescript
but that's something you might encounter if you've done things in R for instance or with data analysis on incomplete datasets
Jean-Louis Giordano
@Jell
Oct 29 2018 09:44
as far as I know, neither structural typing in Elm nor row polymorphism in PureScript allow this. The function signature would be something like {record} -> {record | credit :: Credit}
(I think perhaps Elm used to support this before but they removed that feature?)
I think you'd use something like a HMap type in the end, but that would not be really idiomatic would it? Also quite clunky & verbose?
anyway, I believe that's what Clojure is optimized for
middleware-like functions where data flows & gets enriched
Jean-Louis Giordano
@Jell
Oct 29 2018 09:50
ok the method signature is not really correct, because you can't really express "a record with maybe a key called ssn", at best you'd have "a record with a key that contains a Maybe SSN"
(which I believe is what he was trying to say)
(haven't watched that video in quite a while btw, not his most memorable talk either)
Pierre Krafft
@Zalastax
Oct 29 2018 10:23
Shouldn't that work in PureScript? Typing that in TypeScript should work
Jean-Louis Giordano
@Jell
Oct 29 2018 10:29
it's very possible that I'm wrong, so if you know of a way to implement this in PureScript I'm very interested in being proven wrong!
I'd also like to see how that would look like in TypeScript, I don't know that much about it
jolod
@jolod
Oct 29 2018 12:10
@magthe I'm skeptical of funtool/cats because it uses dynamic binding for the context (e.g. monad instance). That doesn't play well with closures or e.g. defers, I think.
Jean-Louis Giordano
@Jell
Oct 29 2018 12:11

btw, this is what (I think) the type signature would look like using core.typed (to show that it's still possible to statically check this, which I don't think is the issue: you can statically check this, but what would be the cost to do so?)

(All [x]
  [   (I x (HMap :mandatory {} :optional {:ssn String}))
   -> (I x (HMap :mandatory {} :optional {:ssn String, :credit Credit}))])

also warning I haven't actually tried that. I here denotes type intersection (core.typed supports subtyping, but I don't know if structural typing can also solve this. I do not think Row polymorphism could, but I'd be interested in being corrected)

I'll try to write a version in Idris when I get the time because I know you can also solve that with dependent types :p
jolod
@jolod
Oct 29 2018 12:13
@Jell @Zalastax In PureScript you can merge two records; some kind of magic that I don't understand happens. You can express record union types. I think how this works has changed lately. See also https://speakerdeck.com/paf31/an-overview-of-the-purescript-type-system?slide=22 .
Jean-Louis Giordano
@Jell
Oct 29 2018 12:15
aha! that's interesting! do you have an example on how this works and how it would look like?
jolod
@jolod
Oct 29 2018 12:15
I'm not aware of any way to pattern match on the type though, e.g. run one function if you have a credit row, or run another otherwise. You'd use a Maybe for that, and pattern match against Nothing and Just instead.
(Remember that you can pattern match on records in PS.)
@Jell Not really. I used it once, and then removed it. In Clojure it makes perfect sense, but it just didn't fit as well in PS.
I'm not saying it cannot fit well, btw. Just that in my code it didn't.
@Jell To expand on your example. Let's assume that you always have a name. Let's assume you sometimes have an ssn and a credit. Then you can either have { person :: Name, ssnAndCredit :: Maybe { ssn :: SSN, credit :: Money } }, or { person :: Name, ssn :: Maybe SSN, credit :: Maybe Money }.
jolod
@jolod
Oct 29 2018 12:21
But some code might only make use of the name and ssn, and then this ssnAndCredit field (which probably will have to be renamed if another field is added) is just in the way.
Jean-Louis Giordano
@Jell
Oct 29 2018 12:24
sure, but here is the problem being stated by Rich Hickey as I understand it: add tagged values to a set of tagged values in an "open" fashion, where both the tag & value are first class (can be iterated, inspected, merged...). So you would not be able to before hand establish every single combinations of things that might have an :ssn. So I don't think the conversation is "how to model that in PureScript?" but "how does Rich want to model stuff and why does that work in Clojure?"
jolod
@jolod
Oct 29 2018 12:25
Yeah, I'm totally with you.
Jean-Louis Giordano
@Jell
Oct 29 2018 12:25
I think the "let's assume we have a name" is exactly the point: let's not assume that he says
jolod
@jolod
Oct 29 2018 12:25
I was getting to that. Different functions will require different combinations. So the "correct" type would be just unwieldy.
Jean-Louis Giordano
@Jell
Oct 29 2018 12:26
ah! ok right now I'm with you :p
(was too quick to reply :p)
jolod
@jolod
Oct 29 2018 12:27
Yeah, it's been a bit of argumentative tone here, but I wanted to signal that I wasn't arguing by saying that I was expanding on your example. But I realize that was ambiguous. :-)
Anyway, I would go with the flat structure, getting maybes everywhere, rather that trying to encode presence dependencies between different fields.
But the price you pay for that is that when you pattern match, you get all these invalid combinations you need to handle.
Jean-Louis Giordano
@Jell
Oct 29 2018 12:28
bingo! :p
I need to check this record merge in PureScript though that sounds interesting
jolod
@jolod
Oct 29 2018 12:32
It would be nice if you would generalize this function over arbitrary fields: {foo: _, bar: _} <$> r.foo <*> r.bar.
That code would turn { foo :: Maybe a, bar :: Maybe b } into Maybe { foo :: a, bar :: b }.
Jean-Louis Giordano
@Jell
Oct 29 2018 12:38
dug up this piece of code around merging records: https://github.com/doolse/purescript-records/blob/master/src/Data/Record.purs
jolod
@jolod
Oct 29 2018 12:40
Can you also dig up where Union comes from? :-)
Jean-Louis Giordano
@Jell
Oct 29 2018 12:40
yeah that's where I'm stuck :p
That came out after my initial learning phase, so I haven't really looked at it. I was busy solving my problem instead. ;-)
If I understand it correctly, it generalizes Either, so it should be precisely what we're looking for!
Not a completely free lunch though: "But note that polymorphic functions like show or id need to be either annotated or eta expanded due to record impredicativity."
Also, I'm pretty sure Haskell has a similar module. @magthe @ocramz Do you know?
No, actually, variants is not exactly what we want. :-/
jolod
@jolod
Oct 29 2018 12:47
It is "just" an open sum type.
jolod
@jolod
Oct 29 2018 12:53
I can't come up with (good) reasons to use it right now, but that might be because I've trained my mind to think differently. Variants are great if you compare it with using a catch-all pattern, or ad hoc. I'm know I've wanted it, I just can't recall exactly when.
Marco Zocca
@ocramz
Oct 29 2018 14:01
@jolod sorry I wasn't following, what were you looking for?
Marco Zocca
@ocramz
Oct 29 2018 14:37
(though I believe the answer is https://github.com/kcsongor/generic-lens ) </troll>
I think you could use ramda.js or underscore.js for it as well (and they have typings that should be correct) but this is the manual way
Jean-Louis Giordano
@Jell
Oct 29 2018 17:26
cool! thanks for taking the time @Zalastax! could you walk me through some of this? & in the type an intersection type? (so that probably means TypeScript uses subtyping?)
and is there occurrence typing with the hasSSN function? i.e if true v has type HasSSN?
looking at this untagged union here: P | P & HasSsn how will that be unified? will the type checker try to unify on the smaller type first? i.e try to see if P & HasSsn works first?
I think I need to check TypeScript out a bit closer :p fun stuff going on
Jean-Louis Giordano
@Jell
Oct 29 2018 17:32
(also please tell me if I’m way off base)
Jean-Louis Giordano
@Jell
Oct 29 2018 17:50
ok I'm playing with this, this is really cool @Zalastax
Pierre Krafft
@Zalastax
Oct 29 2018 17:51
You're right on all parts! I've never thought about how they do unification but when you have a sum type your code has to work with the least common denominator (in this case P until you refine the type via the type guard (hasSsn)).
Jean-Louis Giordano
@Jell
Oct 29 2018 18:02
very interesting!

ok one downside is the following compiles:

console.log(creditLookup(1))
console.log(creditLookup("1"))

but technically, they fulfill the contract

slightly less good is this one raising a runtime type error:
console.log(creditLookup(null))
can you say the input should be an object?
Jean-Louis Giordano
@Jell
Oct 29 2018 18:08
figured it out: you can add a contraint on P with P & Object, and add the strictNullChecks compiler option
this is neat :)
Pierre Krafft
@Zalastax
Oct 29 2018 18:12
You can also say <P extends object> (It's important that you use lower case object - apparently 1 extends Object...)
Jean-Louis Giordano
@Jell
Oct 29 2018 18:12
:bulb:
nice!
Pierre Krafft
@Zalastax
Oct 29 2018 18:15
TypeScript has a type system that I find very easy to use. It's quite expressive and very out of the way. I hope more languages will follow suite.
jolod
@jolod
Oct 29 2018 20:33
@Zalastax So you have two meetup presentations in the pipeline. One on Erlang as we discussed last week, and one on TypeScript. :-)
Pierre Krafft
@Zalastax
Oct 29 2018 21:03
I suppose. Gonna take a while to figure out the material - I don't have a topic in mind yet.
Erlang is gonna have to be about how the whole architecture and community deals with mutability. Our discussion was somewhat of a revelation for me
Could do a lightning talk on that TypeScript code - otherwise I'll end up rambling about pragmatism
@ocramz really enjoying "Flipping the Bozo Bit"!
Magnus Therning
@magthe
Oct 29 2018 21:09
Yeah, it's good, isn't it?
Jean-Louis Giordano
@Jell
Oct 29 2018 21:11

this is how far I've gotten with PureScript:

creditLookup :: forall before after. Union before (credit :: Maybe Credit) after => { ssn :: Maybe String | before} -> { ssn :: Maybe String | after}
creditLookup person@{ssn: Nothing} = union person {credit: Nothing}
creditLookup person@{ssn: Just ssn} = union person {credit: Just {income: 2000, age: 42}}

Not really sure what's going on at the type level, and haven't managed to have the case where ssn was not a row in the person record. Also something I didn't think of but of course heterogeneous lists are also problematic in PureScript so this doesn't even make sense in PS (as in, doesn't compile):

l = [{ssn: "1234"}, {name: "foo"}, {ssn: "1234", name: "foo"}]

You definitely would have to go with the Maybe everything super-type

Pierre Krafft
@Zalastax
Oct 29 2018 21:15
@magthe only two episodes in but yes! Unitless tests really resonated with me. The system we have at work almost feels like they followed that mantra to its logical conclusion.