bbarker / optics-exercises

The Unlicense
1 stars 0 forks source link

Some optics discussion touching on profunctors and prisms to digest later #1

Open bbarker opened 4 years ago

bbarker commented 4 years ago

liam 2:18 PM So I'm not using Haskell but this is a generic question about optics and this seems like the place where people would know about it: Are optics useful for transformations like those between an XML data representation and a string of that data rep as XML or am I misusing the concept? i.e. is parsing/writing outside the comfortable use case? It seems to me that the semantics should map just like any other data conversion

parsonsmatt:haskell_logo_purple: 2:20 PM yes, this is how conduit-xml-lens works 2:20 you have a prism _Xml :: Prism' ByteString Document. 2:20 the reason you don'treally want to go crazy with this representation is because you can't get good error messages with it

liam 2:23 PM im working in a context where my xml should be well formed pretty much all of the time. I'm just working with an external service that I need to send my internal datastructures to as XML. I don't think this service will be returning malformed XML regularly or anything like that. 2:24 but that's exactly what i'm talking about, i'll check out the implementation in that library 2:24 but the basic intuition is Prism' NotParsed Parsed because every parsed thing can become unparsed but not every unparsed thing can become parsed?

parsonsmatt:haskell_logo_purple: 2:25 PM Yeah. If you take Read and Show classes, then you can get prism readMaybe show :: (Read a, Show a) => Prism' String a. 2:25 (may have messed up the argument order there)

liam 2:26 PM full disclosure i'm working in typescript with fp-ts currently but the concept should map easily enough

parsonsmatt:haskell_logo_purple: 2:26 PM For sure. I think that "optics" is probably the wrong abstraction to think in here; "parsing" is really more appropriate

liam 2:27 PM how do you mean?

parsonsmatt:haskell_logo_purple: 2:27 PM I just did a decent amount of XML parsing using lenses and I ended up writing a separate parser for it to keep track of parse failures and error messages 2:28 If you think in terms of parsing, then you'll immediately have a notion of a "current state", error messages, "position in the structure," and all of these are really helpful when writing parsers and debugging them, even if the input data is always correct and well-formed

liam 2:29 PM right so I'm just highjacking an existing parsing library from npm and trying to wrap it a little to function within my otherwise nicely functionally typed code 2:29 so i'm not actually dealing with any of the parsing semantics 2:30 i.e. i have a function XMLRep -> String and a function String -> XMLRep existing that i can work around

parsonsmatt:haskell_logo_purple: 2:30 PM Yeah, I mean, your parser "primitive" can even be something like parseOptic :: Optic p Document t Element b -> Parser b etc

liam 2:40 PM oh yuck the parser is async meaning I essentially have IO in the middle of this mess 2:41 javascript just because a task can fail doesn't mean you have to use a callback api

masaeedu:speech_balloon: 2:42 PM I think a better approach than to think of parsers as optics is to think of parsers as profunctors 2:42 which can be manipulated using profunctor optics 2:44 this way even if you've got something effectful going on, it still fits the mental model. while standard profunctor optics are pure functions themselves, there's nothing that says the profunctors they adjust have to be

liam 2:48 PM by profunctor we mean one where given: class Profunctor (f :: -> -> *) where dimap :: (a1 -> a0) -> (z0 -> z1) -> f a0 z0 -> f a1 z1 2:48 a1 == z1 and a0 == z0 right? 2:48 (in the case of parsing)

masaeedu:speech_balloon: 2:49 PM i'm not sure I follow what the equality means

liam 2:49 PM same type

masaeedu:speech_balloon: 2:49 PM a Profunctor in Haskell/JS/whatever is something that supports that operation for arbitrary a0, a1, z0, z1 2:49 you're not allowed to restrict what they are

liam 2:50 PM right sure, I just mean if you were to implement it you only have the two types: String and XML or whatever 2:51 i get that Profunctors by definition have 4 type vars that can be whatever

masaeedu:speech_balloon: 2:51 PM by "implement it" do you mean implement dimap itself? 2:52 if so, it'll have to work for any types, we can't restrict it to String and XML (although of course you can instantiate the two type parameters a profunctor takes to whatever when you're constructing an value of the profunctor type)

liam 2:53 PM i mean like if we have a functor it has map :: (a -> b) -> f a -> f b but you could pass it (Int -> Int) and Maybe Int 2:53 and in that case a == b 2:54 in that... there's a word for it but i forget it

masaeedu:speech_balloon: 2:55 PM right. so you implement map :: forall a b. (a -> b) -> Maybe a -> Maybe b for your Maybe type (without being allowed to look at what a and b are), but later you can use it at any two specific types

parsonsmatt:haskell_logo_purple: 2:55 PM yes - in this case you'd have a Parser input output type, and your specific parser would be Parser String Xml. But you could dimap a Parser String Xml to maybe preprocess the input and refine the input and have Parser ByteString RefinedType

liam 2:56 PM right which would "utilize" all 4 type vars 2:56 oh whoa 2:56 that's actually blowing my mind a little bit 2:57 you could dimap a parser into another parser :mindblown: 1

parsonsmatt:haskell_logo_purple: 2:57 PM yeah! just like you can fmap a parser into another one, or bind a parser into another one

liam 2:57 PM and you could keep doing so indefinitely to create a chain of input and output processing :tada: 1

parsonsmatt:haskell_logo_purple: 2:57 PM bingo

masaeedu:speech_balloon: 2:58 PM in a sense this is all optics are. they just help you dimap and first and do a bunch of other fiddling with some arbitrary profunctorial value

liam 2:58 PM oh wait 2:58 so does that mean the differentiation between Prism, Iso, Lens and all that is about like 2:59 which of the profunctor type vars are the same?

masaeedu:speech_balloon: 2:59 PM not quite. it's about what profunctor subfamilies you're allowed to use them on

liam 2:59 PM what's a profunctor subfamily?

masaeedu:speech_balloon: 2:59 PM Isos can be used on any profunctor, so they're quite limited (they can effectively only do what you could already do with dimap ) 2:59 there are a number of subclasses of Profunctor 3:00 like Strong and Choice and so on. there's probably a number of good resources out there if you look up "profunctor optics" 3:01 Strong for example gives you an extra capability like this: class Profunctor p => Strong p where first' :: p a b -> p (a, c) (b, c)

liam 3:01 PM those are tuples?

masaeedu:speech_balloon: 3:01 PM yup

liam 3:02 PM oh 3:02 is this like a cons cell thing

masaeedu:speech_balloon: 3:02 PM it's helpful to try plugging -> in for the p whenever you don't quite understand an abstract profunctor class (edited) 3:04 once you see how it works for functions, it's easy to see how the abstract class generalizes that property of functions to a family of profunctors (edited)

liam 3:06 PM sick. i'll check it out but right now I am supposed to be writing an adapter for an XML service and who knows if "researching profunctors" would qualify as strictly necessary for that in stand tomorrow :sweat_smile: 2

masaeedu:speech_balloon: 3:06 PM fair enough

liam 3:25 PM haha @masaeedu i found a comment of yours on the library im dealing with referring to the exact issue i'm having from 2017 https://github.com/Leonidas-from-XIV/node-xml2js/issues/159#issuecomment-290791792 small world

bbarker commented 4 years ago

Also https://meeshkan.com/blog/profunctors-and-optics/