Live data from Hacker News

Generalizing 'jq' and Traversal Systems using optics and standard monads

chrispenner.ca

51–60 of 102 posts

Re: Generalizing 'jq' and Traversal Systems using optics and standard monads

#51
post #32
post #25

Earlier quoted context omitted.

How would you define the `printMessage` function? The `pet` object doesn't have context about `staff`, and the `filter` function is filtering staff, not pets. It could return staff that have both cats and dogs, so it would incorrectly print dogs.

It was a rough example but the idea is thinking in terms of data in and data out and how can it be done. Here the train of thought first would be: do I have the right data form for the thing I am doing? Here we have: {company: {staff: [{..., pets: []}]}} And what we want to do is to produce a list of all the pet cats with its owner name. [{cat: "bla", owner: "bla"}...] or [{owner: "bla", cats:[...],...}, ...] So I gu…

In javascript:

  const stf = [
      {name: "x", pets: [{type: "cat", name: "kitty"},
                       {type: "cat", name: "kitty2"}]},
      {name: "y", pets: [{type: "dog"}]},
      {name: "z", pets: [{type: "cat", name: "miau"}]},
  ];

  const myTransform = ({ pets, name: ownerName }) => pets
        .filter(({type}) => type === "cat")
        .map(({ name: catName }) => ({ ownerName, catName }))

  stf
      .map(myTransform)
      .flat()
      .forEach(({ownerName, catName}) => console.log(`Cat ${catName} to ${ownerName}`))

Re: Generalizing 'jq' and Traversal Systems using optics and standard monads

#52
post #26
post #22

Earlier quoted context omitted.

Your code is impossible to analyze. Console.log is a side effect. For loops can be easily mapped into functional constructs, but not your snipped does not compute anything. It's not a function.

Oh, you mean using analyzing software. Yeah, I've never had a use for that. On the other hand, you could make my function append to a string and then return the string. Then it wouldn't have side effects so it would be analyzable.

> Oh, you mean using analyzing software. Yeah, I've never had a use for that.

One of the major goals for Haskell programmers (and users of fancy-type-systems in general) is to make it easy to transform runtime errors to compile-time errors. Further, an additional goal (albeit one which Haskell doesn't prioritize as much as some of its relatives) is to make the compiler better at telling the programmer what is wrong and how to fix it. If you have no interest in these projects and don't see why they could lead to code which is more reliable and easier to maintain, then your confusion about why people care about this stuff in the first place is perfectly reasonable IMO.

Re: Generalizing 'jq' and Traversal Systems using optics and standard monads

#54
post #9

Why do you need all this theory when you can use simple imperative languages? for staff in company.staff: for pet in staff.pets: if pet.type == 'cat': print(pet.name + " belongs to " + staff.name) I don't understand why functional languages are used at all.

That exact syntax works for pretty much any Monad. You need the category theory to avoid reinventing the wheel every time.

Re: Generalizing 'jq' and Traversal Systems using optics and standard monads

#55
post #22
post #15

Earlier quoted context omitted.

What's wrong with functions? A function `printCatsBelongingToStaff()` is much easier to read than a line of functional code. I don't understand what you mean by "analyzed much better" and "neat safety guarantees". Is my code hard to analyze or unsafe?

Your code is impossible to analyze. Console.log is a side effect. For loops can be easily mapped into functional constructs, but not your snipped does not compute anything. It's not a function.

[deleted]

Re: Generalizing 'jq' and Traversal Systems using optics and standard monads

#56
post #14

Earlier quoted context omitted.

I'm not sure I'd call LINQ a precursor when LINQ itself was based on haskell's monadic list comprehensions. monadic list comprehensions aren't quite the same thing as optics, however.

List comprehensions aren't quite the same thing; they are declarative but they describe an iterative method of creating lists through maps and filters, whereas optics are semantically oriented around traversals and projections.

isn't that what I said?

Re: Generalizing 'jq' and Traversal Systems using optics and standard monads

#57
post #39

Earlier quoted context omitted.

>When you can get a functional language to type check, it really does Just Work. Not all functional languages have static typing. Also type checking helps, but saying that if the types check out it just work it pushing it IMO. No type checker will catch this error: sqrt :: double -> double sqrt x = x sqrt 10

Dependent types are able to express the constraints that prevent or catch this error. The types of the arguments to the function can have value constraints on it, and those constraints can be determined from a value that exists there: the name of the function.

Not only do dependent types have huge decidability issues, it's my understanding that integrating them with side effects is still an area of active research.

Re: Generalizing 'jq' and Traversal Systems using optics and standard monads

#58

Earlier quoted context omitted.

They're good names for a couple reasons 1. The analogies made do hint at their meanings. 2. But at the same time, the names are more proper nouns than definitions. For something so abstract, it's better to give it an opaque, easy-to-remember name than to give it a "better" name that maybe oversimplifies what it is. This is basically the "Functor" vs "Mappable" argument.

They're practically useless for searching for them. Unsurprisingly trying to co-opt widely used terms tends to be ambiguous and confusing (cf. crypto cryptography, cryptocurrency, and those are at least related). Perhaps the only exception in this group is "data lens", which works as a compound [1]. Guess what optics software does? No ETL, that's for sure. Optics patterns? Uh-oh. Optics development? Nuh-uh. Optics da…

In response to [1], specifically: “Van Laarhoven optics” and “profunctor optics” are qualifiers for two of the more common constructions.

In response to the naming in general: I’m not overly convinced about the naming thing considering that the Go Programming Language is not often confused with the game of Go, nor is the Rust Programming Language with the fungus or iron oxide.

Re: Generalizing 'jq' and Traversal Systems using optics and standard monads

#59
We recently implemented a subset of JQ in Clojure, converting filters and functions into Clojure predicates to allow our users to apply (K)JQ queries directly to Kafka topics holding JSON-like data (e.g Avro, Json, Transit, etc). If it's maps or vectors it works.

The basic implementation is predicate composition as we support multiple combinations of filters.

I hadn't thought any deeper about 'traversal systems' or optics, just parsing a grammar, creating predicates, and composing them. This is an interesting read, perhaps I should take the Clojure impl. further.

https://www.youtube.com/watch?v=7krDIMjVzZM

Probably the most fun coding I've done this year because I had an excuse to use the wonderful Instaparse again.

Re: Generalizing 'jq' and Traversal Systems using optics and standard monads

#60
So overly complicated. The "state of the art" of FP now means heavy type systems and heavy machinery to deal with them. The 80/20 pareto of FP is just pure functions + composing those functions and it works in any language. Here is all of it in python:

    from __future__ import print_function
    import json

    struct = json.loads(open('staff.json','r').read())
    staff = struct['staff']
    salaries = struct['salaries']

    def visit_pets(staff, cond, visit):
        for person in staff:
            for pet in person["pets"]:
                if (cond((person, pet))):
                    visit((person, pet))

    # Find all cats owned by any of our employees
    visit_pets(staff, lambda t: t[1]["type"] == "cat", lambda t: print(t[1]))

    # Find each pet and their owner
    visit_pets(staff, lambda t: True, lambda t: print("{} belongs to {}".format(t[1]["name"], t[0]["name"])))

    # Give a $5 raise to anyone who owns a dog
    dog_owners = set()
    visit_pets(staff, lambda t: t[1]["type"] == "dog", lambda t: dog_owners.add(t[0]["id"]))
    for staff_id,salary in salaries.items():
        if staff_id in dog_owners:
            salaries[staff_id] += 5
In my view it gets you 80% of the power of optics in exchange for zero machinery and can be understood by almost anyone. For doing these kinds of ops on a deeper structure, using Scala/Javascript or something with first-class support for HOFs, folds/filters/maps would work better.

I wish more people promoted this view of FP - just pure functions, higher order functions and referential transparency. It gives you the ability to reason locally and extend code well. The remaining type system and architecture astronomy buys very little in relation to what it costs.

Post reply on HN