dhall-lang icon indicating copy to clipboard operation
dhall-lang copied to clipboard

Turn List.map into a builtin

Open sjakobi opened this issue 6 years ago • 12 comments
trafficstars

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…

sjakobi avatar Sep 13 '19 07:09 sjakobi

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 ?

Nadrieril avatar Sep 13 '19 07:09 Nadrieril

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.

sjakobi avatar Sep 13 '19 07:09 sjakobi

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.

Gabriella439 avatar Sep 13 '19 15:09 Gabriella439

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.

singpolyma avatar Sep 14 '19 23:09 singpolyma

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.

PierreR avatar May 17 '20 10:05 PierreR

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.

PierreR avatar May 17 '20 15:05 PierreR

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

Gabriella439 avatar May 17 '20 15:05 Gabriella439

Alright looks like I can do this:

let list/map =  (../../Prelude.dhall).List.map
let list/map =  (../../Prelude.dhall).Optional.map

I 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 avatar May 17 '20 16:05 Nadrieril

@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.

PierreR avatar May 17 '20 16:05 PierreR

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 avatar May 17 '20 16:05 PierreR

@PierreR: See: https://github.com/dhall-lang/dhall-lang/issues/382

Gabriella439 avatar May 17 '20 17:05 Gabriella439

@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.

PierreR avatar May 17 '20 19:05 PierreR