3 ms·
I wrote some batch (list) oriented code for a static analyzer recently. It was great until I decided to change my AST representation from a tuple+discrimated u
by ryanjshaw 3y ago
I wrote some batch (list) oriented code for a static analyzer recently.
It was great until I decided to change my AST representation from a tuple+discrimated union to a generic type with a corresponding interface i.e. the interface handled the first member of the tuple (graph data) and the generic type the second member (node data).
This solved a bunch of annoying problems with the tuple representation but all list-oriented code broke because the functions operating on a list of generics types couldn't play nice with the functions operating on lists of interfaces.
I ended up switching to scalar functions pipelined between list functions because the generic type was more convenient to me than the list-oriented code. The reality is you often need to play with all the options until you find the "right" one for your use case, experience level and style.
- aeonik 3y agoI'm curious, why couldn't the list of generic types play nice with functions operating on lists of interfaces?
- deleted 3y ago[deleted]
- ryanjshaw 3y agoHave a look here: https://onecompiler.com/fsharp/3ztmx2uhr https://onecompiler.com/fsharp/3ztmx2uhr Basically we want a "Yes" or a "No" when the family has children: let kidsYN = family |> numberOfChildren |> yesOrNo But we get: error FS0001: Type mismatch. Expecting a INode list but given a Node<Person> list The type 'INode' does not match the type 'Node<Person>' Forcing us to do: let familyAsINode = family |> List.map (fun n -> n :> INode) Sure you can wrap this up in a function but it's ugly and annoying to have to use this everywhere and takes away from your logic. It ends up being better to split your "batch" and "scalar" operations and compose them e.g. by introducing a "mapsum" function: let kidsYN2 = family |> mapsum numberOfChildrenV2 |> yesOrNo