Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
RFC: List append operator proposal #68
This is a request for comments on a proposal to add support for a list append operator
Right now there is no list append operator. The closest thing Dhall has to such an operator is
However, there are some times when all you want to do is to append two lists. For example, I ran into this issue when trying to translate the second Jsonnet example on this page (and that was the original motivation for this RFC), where I had to append two lists and awkwardly had to use something like
The reason that
There is also a separate and orthogonal issue, which is what to name this list operator if Dhall were to provide support for one.
So I will structure this three sets of orthogonal proposals for:
In each set of proposals, I will also indicate which one I prefer. I also invite people to submit proposals of their own if they can think of a better way to support this.
Proposal set (A) - How to support the list operator
Proposal (A0) - Do nothing
Maybe this isn't such a big deal and users can live with using either
Proposal (A1) - Add support for user-defined infix operators
Allow users to define new operators using the syntax:
let (++) = ./Prelude/List/append SomeType in ...
... and add
Proposal (A2) - Add a built-in list concatenation operator
This would add a new
I prefer proposal (A2).
I don't recommend proposal (A1) because a user-defined list operator cannot take advantage of type inference. The type of
∀(a : Type) → List a → List a → List a
... so you would need to instantiate
I also don't think it hurts to add a new list append operator to the language since it's very simple to implement and I'm pretty sure some users will want to use this operator. It also is associative and has an identity, so it passes the bare minimum criteria for being an operator in Dhall. So this is why I don't think we should do nothing as in Proposal (A0).
Proposal set (B) - Name of the append operator
If we go with proposal (A1) then we should recommend a naming convention for this operator and if we go with proposal (A2) then we have to pick a name.
Proposal (B0) - Reuse the operator
class Alternative f where (<|>) :: f a -> f a -> f a empty :: f a
The reason that it relates to
-- Assuming that we have: instance Alternative F where ... -- ... that implies that we also have: instance Applicative F where ... -- ... because `Applicative` is a superclass of `Alternative` -- ... which means that we can also define this `Monoid` instance for `F`: instance Monoid a => Monoid (F a) where mempty = pure mempty mappend = liftA2 mappend
... and if you have the above
In other words,
I think the proposal looks fine. I just want to comment briefly on your remark on overloading.
Overloading the operators the way you suggest above would mean that the operator (**) would behave as bog standard concatenation for text but as an outer product for lists. That would be terribly confusing. If you want to introduce overloading, I'd rather you made sure that each operator intuitively has the same behavior for every type it is defined for.
x ** (y ++ z) = (x ** y) ++ (x ** z)
Giving it outer product semantics is consistent with that law
The semantics of
Perhaps you can explain why you think that the distributivity law is important in this context. I certainly don't understand why it needs to be there.
To be clear, here's the law that I think would make sense:
It involves the as yet undefined, but intuitively sensible, function
My reasoning goes something like this:
For the type
We obviously have to support appending lists (that's the whole point of this proposal!), but some users (like myself) would also like to be able to append elements. For example, I would like to be able to write something like this:
let Text/concat = ... in let names = ["John", "Lucy", "Brad"] in let greeting = ["Hello", "Goodbye"] in Text/concat (greeting ** [" "] ** names ** ["!\n"])
The whole reason for having two separate operators for appending
As a bonus,
Regarding functor laws, the laws that I am proposing if there were a hypothetical
textToList (x ** y) = textToList x ++ textToList y textToList "" =  : List Char
I agree with everything you say up to the point of reusing the
Furthermore, the fact that
My reasoning is that operator reuse is good as long as the operator obeys mathematical laws. The reason why is that if the operator obeys laws then you can reason abstractly about the code's behavior independent of what type you instantiate the operator to work on.
The point of reuse is not just to avoid wasting operator namespace. It's also about preserving mathematical intuitions as we transition between different types. The whole reason that they are named
-- Given: zero =  : List Text one = [""] x, y : List Text List/length Text (x ++ y) = List/length Text x + List/length Text y List/length Text zero = +0 List/length Text (x ** y) = List/length Text x * List/length Text y List/length Text one = +1
However, that still doesn't address the separate question of whether
In this case, I prefer to view
Alright, but I have just one last question before you go: the strongest alternative approach that I'm still considering is using
added a commit
Jun 30, 2017
added a commit
Jul 15, 2017
I ended up reverting this change temporarily because using the same operator name is problematic for downstream compilers (such as
For now I'll just reopen this issue until I figure out how to resolve this. Most likely I will just end up selecting a unique operator and constructor name for appending