These are chat archives for ramda/ramda

14th
Dec 2018
Alexander Lichter
@manniL
Dec 14 2018 14:57

Is there a way to use head and tail of a list as argument for a function call while you only pass in the list "as is" in?

Example:

const findStuff = object => find(eqProps('x', object))

const list = [ {x:1}, {x:2}, {x,3}]

// Call it like but in "one line", maybe with Ramda head/tail functions)
const [head, ...tail] = list
findStuff(head)(tail)

// Ideas I had 
R.converge(findStuff,[R.head, R.tail])(list)
Scott Sauyet
@CrossEye
Dec 14 2018 17:34
@mannil: Something like this?
const findStuff = (object, list) => find(eqProps('x', object), list)
const list = [ {x: 1, y: 'foo'}, {x: 2, y: 'bar'}, {x: 1, y: 'baz'}, {x: 3, y: 'qux'}]

R.converge(findStuff, [R.head, R.tail])(list) //=> {x: 1, y: 'baz'}

converge is not going to work with that sort of curried function, as (the binary version of) its API looks like

converge(f, [g, h])(x) //=> f(g(x), h(x))

and not f(g(x))(h(x)

Scott Sauyet
@CrossEye
Dec 14 2018 17:39
But note that if you use converge in this simple manner, it's often better to use the more standardized lift:
const findStuff = (object) => find(eqProps('x', object))

const list = [ {x: 1, y: 'foo'}, {x: 2, y: 'bar'}, {x: 1, y: 'baz'}, {x: 3, y: 'qux'}]

R.lift(findStuff)(R.head, R.tail)(list) //=> {x: 1, y: 'baz'}
This one does work with that curried function.
Rakesh Pai
@rakeshpai
Dec 14 2018 17:51
@evilsoft Sorry for the late response - travelling. Thanks! How can I learn more about this? Could you give me some links I can read up on? Or is it just 'learn Haskell'? :D
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 18:47
So all of this I cobbled together from a couple of sources from (2) branches of math: Set Theory and Category Theory. If you primarily use ramda, I would recommend learning Set Theory, but if you set out to use things like ramda-fantasty, folktale or crocks then you will wanna do a dive into Category theory. Most of what I know comes from these sources:
I also recommend getting a small white board to play around with the example and exercises
You can look at haskell examples and tutorials, but they tend to be very specific and do not teach to the more General bits that come from category theory, so I ended up with partial intuitions on these structures
Oh also learning some Group Theory (or Abstract Algebra) is a bonus, so you can get to know things like Groups, Semigroups, Monoids and Rings
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 18:56
Also the problem with haskell -> JS is the type system that Haskell provides. In JS we have much more power with what is called Ad-hoc Polymorphism, so we have to do things a little different and if we follow the Haskell way of thinking you end up with icky things like TypeScript or Flow and end up not leveraging the power of a dynamic language like JavaScript. Do not believe the hype about Dynamic Typing being a bad thing. When people say...studies have show...Stop them right there and ask, "What studies?" and if they can actually provide them, I think you will be suprised at how weak those studies are with their sample size, control groups, instrumentation and p-values for error correction.
While yes static typing can make some things easier, it effectively removes a lot of power from JS.
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 19:10
Don't get me wrong TypeScript is AMAZING for OO programming, but falls down a wee bit when you go fully functional JS
Scott Sauyet
@CrossEye
Dec 14 2018 19:13
@evilsoft: That very much captures my feeling about Typescript/Flow: they have their places, but they bring real disadvantages alongside their advantages. When trying to do more FP in JS, they are at best a distraction.; more likely they actively get in your way. I've used that, "What studies?" line with senior management types, and they get really flustered. :smile:
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 19:17
Goodness, I wish gitter allowed for reactions, I would give that a big ol :+1:
Scott Sauyet
@CrossEye
Dec 14 2018 19:31
:smile:
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 20:15
My wife has a wonderful saying that goes something like: "Sure Type Systems feel more correct, But do I want to be the "rightest" person whose competitor already shipped their product"
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 20:22
Aslo @rakeshpai quick correction, I said "p-values for error correction", that is a bad way to think about p-values, they are used to determine if your collected data proves the null hypothesis and have nothing to do with error correction. In the 3 studies I have seen the provided p-values seemed very deflated to account for what I feel was poor data collection and instrumentation implementation.
The other 2 studies that were provided to me where meta analysis on two of the other three studies I was provided
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 20:27
Not to mention the subjects were all experts to novices in Statically Types Languages, and not a one were people that shown even the slightest knowledge in using a Dynamic Language and not once used patterns like reasonable defaults or other techniques we as dynamic programmers would use to restrain our power where appropriate. I will see if I can hunt them down, a lot of my subscriptions for Journals have expired when I stopped doing contract work For the University of Arizona
The work sucked, but the access to knowledge was an amazing benefit.
Anyway, Hope this all helps, apearently I was in a chatty mood today and destroyed the poor Ramda channel with a wall of opinions. Sorry about that you awesome people
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 20:53
Oh, but before I go, one last quick argument for Parametric Polymorphsim: In JS, if I make the claim that a given function fis what is called a Natural Transformation, I will NEVER be able to provide that proof. In Haskell that proof is automatic because it will not compile if it is not. Also same goes for claims like "Pair is a Limit". And without PP, in JS we can never provide an abstraction of an End/Coend, because we would have to provide a case and product /co-product type that accounts for EVERY type that can be conceived in JS. But really the only thing that matters or that we will encounter more often is the Natural Transformation bit, but we will just have to play the trust me game.
This only becomes a problem for people who make ADT libraries and is not encountered too much in our applications, unless you are making your own types with things like union-type or daggy. And even then, using abstractions like Limit/Colimit and End/Coend are very few and far between
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 21:00
As library maintainers we try our best to hide all those messy things like claims, proofs and diagrams from the end user so they do not have to worry or think about such things.
Robert Mennell
@skatcat31
Dec 14 2018 21:50
@evilsoft @CrossEye why don't you guys like TS for FP? I use TS just fine for higher order types and FP. It's kind of like using Sanctuary in combination with Ramda
Robert Mennell
@skatcat31
Dec 14 2018 21:56
after reading the semiWall definition (heh) I get to the conclusion that you like the AdHoc polymorphism power of JS and Python and don't like the strongly typed implementations of things like Haskell and Pure/TypeScript because you lose a lot of the flexibility of being able to provide safe defaults and alternative logic that you get with property/field checks on the direct object or proto chain(Object.hasOwnProperty('key') versus obj[key] or instanceof)
Asad Saeeduddin
@masaeedu
Dec 14 2018 22:03
@skatcat31 it just makes things impossible that work fine in JS. I don't mind enabling TypeScript and just letting it analyze my JS in the background, sometimes it can provide some good intellisense, but for the most part it just can't keep up
Ben Briggs
@ben-eb
Dec 14 2018 22:05

I quite like this article https://www.williamjbowman.com/blog/2018/01/19/untyped-programs-don-t-exist/

Instead of arguing about untyped vs typed, a non-existent distinction, we should accept that all programs have invariants that must be obeyed, i.e., all programs are typed. The argument we must have is about the pragmatics of types and type checking.

Asad Saeeduddin
@masaeedu
Dec 14 2018 22:06
here's an example i remember from a while ago when i was still posting long screeds in the typescript issue tracker: Microsoft/TypeScript#19800
in JS the code is:
const match =  (p, ...c) => {
  const cases = new Map(c)
  return x => cases.get(p(x))(x)
}
which lets you do for example:
const validate = match(
  x => x.foo.bar > 5,

  [false, x => "number must be more than five!"],
  [true,  x => "ok"                            ])
in TypeScript my best attempt (which still doesn't work along several dimensions), was:
type Match = <P extends string, X extends {[p in P]}, R>(
    x: X,
    p: P,
    f: {[_ in X[P]]: (data: X & {[p in P]: _}) => R })
    => R
declare const match: Match

declare const x: { foo: "a", bar: 42 } | { foo: "b", baz: 42 }
match(x, "foo", {
    a: x => x.bar,
    b: x => x.baz
})

@ben-eb "Types are invaluable to developing my programs, but your ‘typed’ language prevent me from writing down my types!’"

So much this

Ben Briggs
@ben-eb
Dec 14 2018 22:09
@masaeedu Yeah. I think I have to bring this article up whenever typescript is mentioned :)
Robert Mennell
@skatcat31
Dec 14 2018 22:27
@masaeedu I'm a little confused since your function merely needs a type signature?
const match = (p : Function, ... c : Array< [any, Function ]>) : Function => {
  const cases : Map< any, Function > = new Map(c);
  return x => cases.get(p(x)(x));
}
or were you also trying to get it to inference types of the conditional outputs?
Asad Saeeduddin
@masaeedu
Dec 14 2018 22:28
@skatcat31 If I'm going to be using any everywhere the type system is pointless. Moreover, if I'm using noImplicitAny, I'm going to keep having to introduce and eliminate that in various places
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 22:28
Please do not get me wrong, if it works for you and your team and the product/market your company is into, then it is not a bad thing. As people have already stated, it makes a lot of really simple things VERY complicated. I love the compose and curry helpers a lot of libraries provide for JS but still have not seen any really good way to use them without just using some TS version and skipping say the ramda versions
the point is that any is in all the FP TS code I have seen, so what is the point
Asad Saeeduddin
@masaeedu
Dec 14 2018 22:29
it's not about inference, although that's a separate pervasive problem with TypeScript. Even if we accept that we need to add type annotations everywhere, the system is simply not expressive enough to describe the type of that function
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 22:29
oh nm... @masaeedu already mentioned that
I also feel that we have some really great minds dealing with the patterns generated using PP in wonderful languages like Haskell, but if we try to port those concepts and ideas to JS, then we lose the benifit of taking the road of AP and seeing what we can do with these types of systems, having a strong functional language like JS that is also Dynamic should lead to some interesting takes on doing this sort of thing in another arena
Robert Mennell
@skatcat31
Dec 14 2018 22:37
@masaeedu then you would limit your key space. Maps by definition in JS allow anything to be a key, and you could instead limit it in your typescript to explicit cases. The implicit any return of the closure would need to be limited by you, again if you wanted to
const match = (p : Function, ... c : Array< [Boolean | string, (x : any) => string] >) : Function => {
  const cases : Map< Boolean | string, (x) => string > = new Map(c);
  const ret : Function = (x) : string => cases.get(p(x))(x);
  return ret;
}
Asad Saeeduddin
@masaeedu
Dec 14 2018 22:37
This is also wrong, since the function isn't limited to working with booleans
for example:
const max = match(
  a => !a.length ? "0" : a.length === 1 ? "1" : "n",

  ["0", ()          => fail("No max of empty list")               ],
  ["1", ([x])       => x                                          ],
  ["n", ([x, ...y]) => { const m = max(y); return x > m ? x : m; }])
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 22:40
btw @masaeedu :heart: dat match, very eloquent for a rough example
Robert Mennell
@skatcat31
Dec 14 2018 22:41
of course due to the implicit nature you could also just let intellisense figure it out:
const match = (p : Function, ... c : Array< [Boolean | string, (x : any) => string] >) : Function => {
  const cases = new Map(c);
  return (x) => cases.get(p(x))(x);
}
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 22:43
@ben-eb great article, never read that before
Robert Mennell
@skatcat31
Dec 14 2018 22:44
otherwise you would just put Function as the second type and then the developer who uses match would then get to put their typing into their functions instead of you explicitly enforcing it
@ben-eb currently reading article, don't think I'm ignoring it
Asad Saeeduddin
@masaeedu
Dec 14 2018 22:45
@skatcat31 I'm not really sure I'm following what you're saying. There should be no Boolean in that type signature, given that this function isn't specifically for matching on booleans (otherwise I'd just use a ternary operator)
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 22:46
I mean if we are compiling to JS from a language, I would personally pick PureScript over TypeScript, it has a type system made for FP and not just trying to convert something used in OO to a type system for FP
Asad Saeeduddin
@masaeedu
Dec 14 2018 22:46
Thanks @evilsoft. I stuck it on npm if you ever need to use anything like that: https://github.com/masaeedu/natch. Of course it's easy enough to define it yourself if you need
Ian Hofmann-Hicks
@evilsoft
Dec 14 2018 22:47
incoming star :star:
Robert Mennell
@skatcat31
Dec 14 2018 22:48
@masaeedu it was an adaption to show that there could be manual limiting of key for the Map type versus just plopping in an explicit any
Asad Saeeduddin
@masaeedu
Dec 14 2018 22:49
the problem is that you can't manually limit it to any specific types, you need to infer the types inductively from the heterogeneous argument list, and collapse those into a union that is the output type of the function
which requires type families, good inference, support for parametric polymorphism, and probably a great number of other features besides
you could write several PhD theses adding all those features to an existing type system
all to support the denotation of the following code:
const match =  (p, ...c) => {
  const cases = new Map(c)
  return x => cases.get(p(x))(x)
}
we can of course say "oh, golang doesn't have generics, what's the problem? just write id_string :: String -> String and id_int :: Int -> Int and so on", which is a valid workaround, but above a certain barrier of abstraction the number of workarounds start overwhelming the actual logic you're trying to write
Robert Mennell
@skatcat31
Dec 14 2018 22:53
@masaeedu (foreward: I do not dislike your match helper, it's actually a fairly well reasoned way of laying out transformations) we're having a break down in communication of what I'm attempting to say versus what you're interpreting. I'm not sure whether this is caused by how I'm trying to explain it or if it's personal bias
@masaeedu golang has the empty interface
so if you want to duck case in golang and accept anything unsafely you easily can
Asad Saeeduddin
@masaeedu
Dec 14 2018 22:56

@skatcat31

I'm not sure whether this is caused by how I'm trying to explain it or if it's personal bias

I'm probably misinterpreting what you're saying, since my TypeScript reading skills have atrophied somewhat. Could you put what you're trying to say a different way?

I don't want to do anything unsafely there, in fact I want to express the identity function, which is so constrained that there can only ever be a single term of that type
Robert Mennell
@skatcat31
Dec 14 2018 22:57
I'm attempting to point out that TypeScript allows for enforcement through interpreted types beyond just using any by a conscious and defined developer choice
when were you trying to express the identity function?
Asad Saeeduddin
@masaeedu
Dec 14 2018 23:00
The example of type annotations that you mentioned is inapplicable to the function we're discussing
The function match accepts a function from some value a to a union of a row of types. It additionally accepts a heterogeneous list of pairs, the first elements of which correspond to the aforementioned row of types. The second element of each pair is a function that carries a to some result type. Finally, it returns a function from a to a union of the result types of the second elements of the heterogeneous list
in order to actually usefully type match (in other words, express a type signature for it in such a way that the compiler corrects the programmer, and not the other way around), we would need a type system with several features that TypeScript doesn't have
Asad Saeeduddin
@masaeedu
Dec 14 2018 23:06
There are definitely type systems out there that have all these features, I'm not saying it's impossible. For example in Idris you could define such a function and wouldn't need to introduce a patchwork of workarounds where you're falling in and out of the type system
Robert Mennell
@skatcat31
Dec 14 2018 23:06
const match = <T, V>(p : Function, ... c : Array< [T, Function] >) : Function => {
  const cases = new Map(c);
  return (x : V) :T => cases.get(p(x))(x);
}
Asad Saeeduddin
@masaeedu
Dec 14 2018 23:07
Function is the problem here
and the any-s
we can continue this in direct chat btw if you're interested, this is sort of off topic for the room
Robert Mennell
@skatcat31
Dec 14 2018 23:07
that works
Asad Saeeduddin
@masaeedu
Dec 14 2018 23:08
nope, you still have Function
which does not constrain p to produce a union of the types occurring in the first elements of the pair list
nor does it constrain the result to produce a union of the types occurring in the result type of the second elements of the pair list
(also the second elements of the pair list are not constrained)
Robert Mennell
@skatcat31
Dec 14 2018 23:10
we'll continue in private