dhall-lang
dhall-lang copied to clipboard
Turn List.map into a builtin
In https://github.com/dhall-lang/dhall-lang/issues/552 the idea was brought up to add a List/map as a builtin, since some implementations (like Haskell) have so far had difficulties to make it perform well.
If there are no objections, I would make a proposal…
I'd be interested to see real-world examples where this is noticeable. Maybe we could add some of those examples to the test suite ?
I'd be interested to see real-world examples where this is noticeable.
Me too! @fizruk linked to a sample in https://github.com/dhall-lang/dhall-lang/issues/552#issuecomment-515115041, but it's sadly already normalized and rather convoluted.
So I've gone back and forth on this issue (in separate threads I've argued both for and against List/map). However, my current thoughts are that adding new language bindings is one of the current bottlenecks for growth of the language, and adding a List/map built-in is probably easier for a new language binding than optimizing List/build+List/fold to be more efficient.
My objection is already noted in #552 since the whole premise of that ticket was that we could stand to do the opposite on some existing built-ins.
I, too, am interested in how many real-world cases this would improve for today's users with today's implementations.
It has probably been considered before but there is a slight user convenience with a buit-in map.
It doesn't seem possible to write this:
let List = (../../Prelude.dhall).List
in
List.map ...
../../Prelude.dhall defined the version of the Prelude used for the project
So I basically ended up with
let map = (../../Prelude.dhall).List.map
but now I have a problem if I want to use both Optional.map and List.map in the same file.
Anyway the lack of a built-in List/map & Optional/map has been quite surprised for me as a beginner.
Alright looks like I can do this:
let List/map = (../../Prelude.dhall).List.map
let Optional/map = (../../Prelude.dhall).Optional.map
I am new to Dhall so still wondering if Dhall could have a polymorphic functorial map.
So I've been thinking about adding something like this in another context: adding with support for descending into Lists, like this:
{ x = [ { y = 1 }, { y = 2 } ] } with x.*.y = 3
It's easier to standardize if the language has built-in support for map
Alright looks like I can do this:
let list/map = (../../Prelude.dhall).List.map let list/map = (../../Prelude.dhall).Optional.mapI am new to Dhall so still wondering if Dhall could have a polymorphic functorial map.
You can also do
let list = (../../Prelude.dhall).List
And then use list.map. The only problem with your previous attempt is that List is a reserved name. Any other name would work fine.
Dhall can have a polymorphic functorial map, but there's no type inference so you wouldn't really gain anything. You'd have to write something like map Prelude.List.Functor ... instead of Prelude.List.map ..., which isn't much better.
@Nadrieril Thanks for the explanation. I am now using
let List/map = (../../Prelude.dhall).List.map
let Optional/map = (../../Prelude.dhall).Optional.map
which mimics builtins perfectly.
If I start using a lot of Prelude functions from List or Optional I guess I will follow your suggestion.
FWIW what seems like a regular operation on my side is Optional/map.
let makeContainer
: Container.Type → openshift.Container.Type
= λ(c : Container.Type) →
openshift.Container::{
, name = c.name
, ports =
Optional/map
(List Natural)
(List openshift.ContainerPort.Type)
( List/map
Natural
openshift.ContainerPort.Type
( λ(port : Natural) →
openshift.ContainerPort::{ containerPort = port }
)
)
c.ports
}
in makeContainer
@PierreR: See: https://github.com/dhall-lang/dhall-lang/issues/382
@Gabriel439 is it ok to go on and somehow hijack the thread ? Please let me know if it is a problem. I can repost in discourse.
Anyhow in this case I am already using default:
let Container =
{ Type =
{ name : Text
, image : Text
, envVars : Optional (List openshift.EnvVar.Type)
, runAsRoot : Bool
, runPrivileged : Bool
, ports : Optional (List Natural)
, volumeMounts : Optional (List openshift.VolumeMount.Type)
}
, default =
{ runAsRoot = False
, runPrivileged = False
, envVars = None (List openshift.EnvVar.Type)
, volumeMounts = None (List openshift.VolumeMount.Type)
, ports = None (List Natural)
}
}
in Container
I would like to keep the default to None for the ports because in general I want to let the user the ability to omit the definition completely (and it then gets the none value thanks to the default)
That said the link you sent is quite interesting. I didn't know that the first use case of default was all about Optional value. I will see if I can come up with something else using default on a more specific field.