Live data from Hacker News

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

chrispenner.ca

31–40 of 102 posts

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

#31
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.

How would you abstract code so that you can also update pet's name in addition to the above statement while minimizing duplicate code? I think that is one of the key things about lenses.

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

#32
post #25
post #23

Earlier quoted context omitted.

Because it is easier to reason about data transformations. Here when I see loops and conditionals it is confusing because anything could be happening. company.staff.filter(notCats).forEach(printMessage) has more information of what is going on for example imho. But there is a lot more to it.

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 guess what Im trying to explain is that what is important is the change in the way of thinking about problems. When you think of them as data transformations there is a whole lot of possibilities that open. And it is not more expensive because you even have things like transducers. When you abstract away the iteration you are able to compose much more easily.

And to answer your question directly in my example the print function would print for each cat belonging to the staff which is not great flattening would be more elegant. What I wanted to convey is thinking in data transformations and abstracting away the implementation details of the iteration.

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

#33
post #14

Earlier quoted context omitted.

LINQ is definitely a precursor, and has tons of fantastic ideas. It's unfortunate that Microsoft didn't continue to invest heavily into this as it's rarely mentioned nowadays.

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.

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

#34
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.

The theory lets compilers catch a ton of bugs at compile time. When you can get a functional language to type check, it really does Just Work. And when you limit side-effects, you know another function isn't going to monkey with something it's not supposed to.

That said, I'm presently struggling with AST transformations and other fun stuff in Haskell, so I completely agree with stating it imperatively. It's far, far easier to learn and read simple imperative code.

That's why in my language Tenet[1] I'm trying a hybrid approach: the underlying representation is immutable, but the language uses ordinary control flow and mutation.

[1]: https://tenet-lang.org/

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

#36
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.

Don't be pedantic, this is super simple code that is easy to analyze. Side-effects are not the terror that haskell programmers make of it.

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

#37

"Optics" and "lenses" seem like such bad names just to be able to make a "view data through a lens" pun.

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.

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

#38

Earlier quoted context omitted.

I wonder how these "optics" compare to .NET's Language Integrated Queries (LINQ) (see https://en.wikipedia.org/wiki/Language_Integrated_Query )

LINQ is definitely a precursor, and has tons of fantastic ideas. It's unfortunate that Microsoft didn't continue to invest heavily into this as it's rarely mentioned nowadays.

Wikipedia claims there are LINQ implementations for Javascript/Typescript and for PHP (in addition to C#, of course).

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

#39
post #34
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.

The theory lets compilers catch a ton of bugs at compile time. When you can get a functional language to type check, it really does Just Work. And when you limit side-effects, you know another function isn't going to monkey with something it's not supposed to. That said, I'm presently struggling with AST transformations and other fun stuff in Haskell, so I completely agree with stating it imperatively. It's far, far…

>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

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

#40
post #15
post #11

Earlier quoted context omitted.

Functions are composable. Your statement is not. If I would ask you to create a snippet that searches for cats, another one that searches for dogs and a third one that searches for staff that owns both a cat and a dog you will either duplicate a lot of code or end up with functions. Now if you restrict yourself to not introduce any side effects, the interpreter can be proven correct, the execution can be analysed muc…

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?

Setting aside for the moment that optics themselves are (edit: in many implementations, at least) just functions, albeit of a somewhat different flavor than `printCatsBelongingToStaff`...

Functional programmers (of the Haskell bent, at least) would prefer that they not have to write printCatsBelongingToStaff, printCatsBelongingToCustomers, printCatsBelongingToChildrenOfStaff, printDogsBelongingToStaff, renameCatsBelongingToStaff, &c. They would instead prefer to write print, rename, staff, customers, cats, dogs (or perhaps just pets, with an ability to discriminate between cats and dogs...), children, and compose. They would also prefer to write these functions in such a way that the computer (rather than the human) can analyze the resulting compositions and guarantee that they operate in the way you would expect (with "expect" being precisely codified between the types of the functions and a succinct set of laws).

The big picture here is that computers are much faster and more consistent in their checking ability than humans, so the more things we can encode in our programs in a computer-checkable way, or in laws that we can prove are preserved under composition, the more reliable our software will be on whole.

Post reply on HN