Making imports more pleasant

699 views
Skip to first unread message

Evan Czaplicki

unread,
Feb 7, 2015, 12:17:50 PM2/7/15
to elm-d...@googlegroups.com
Since 0.14, I have been hearing more complaints about imports. Lots of different proposals and discussions. Beginners struggling to understand how the different variations fit together. After hearing Laszlo's take on this, I am trying out a new syntax and semantics.

Here are all the variations of the syntax:

import List
import List exposing (map, filter)
import List exposing (..)

import List as L
import List as L exposing (map, filter)
import List as L exposing (..)

The most important change is in semantics though. In cases 1-3 you are able to refer to anything in the List module as List.member and List.isEmpty. So importing a module implies that you want to be able to use it's name to qualify all of its values.

In cases 4-6, it is the same except you can qualify with L but not List. Why hide List though? There may come a time when you don't want name overlaps with modules, so this makes it possible to avoid collisions.

Overall, I think this meets my goals for this change:
  • All imports are explicit and easy to find. No one can introduce a dependency on line 1500 of a file.
  • There will be less duplication in the import section. No importing the same thing twice in different ways.
  • It will be easier to understand for beginners.
  • It somewhat discourages exposing by making it longer.
Remaining Questions:
1) What happens when there are multiple imports of the same module?
  • a) Sean proposed making everything additive. This would make hiding any defaults impossible.
  • b) Maybe we can just disallow multiple imports of the same module?
2) How this should interact with the current default imports?
  • a) Anyone can refer to Basics, List, Maybe, Result, and Signal from anywhere? Seems weird.
  • b) Certain values are exposed, but the modules are not available for making things qualified.
It seems like 1.b and 2.a fit together best. We could make it so user-defined imports always override default imports and there are no duplicate user-defined imports.

But maybe there are other alternatives?

Joey Eremondi

unread,
Feb 7, 2015, 12:27:08 PM2/7/15
to elm-d...@googlegroups.com
This is cool, and I like the idea that you can always use (List.whatever) to refer to something

Honestly, though, I don't know if this addresses people's main complaints. The two biggest issues I've noticed are:

1. Infix operators are a pain to use, since each one of them needs to be imported explicitly, or the whole module imports
2. So little is imported by default (qualified or unqualified) that there's tons of boilerplate just to get anything basic to compile

Problem 1 is fixed by either having syntax for "import all infixes" (which needs implementation) or by importing them all implicitly (which causes conflicts).

Problem 2 is solved by either having IDE support for automatic imports, having a broader set of things imported (qualified) by default, or having some sort of import-inference.

I apologize if I'm misreading peoples opinions.

--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Evan Czaplicki

unread,
Feb 7, 2015, 12:35:08 PM2/7/15
to elm-d...@googlegroups.com
On 1, are you talking about (<~) and (~) specifically? No other operators come to mind for me. It sounds like a more specific concern than the way you have phrased it. Is that possible?

On 2, I think this is potentially addressed by the ideas in the "Remaining Questions" section. If default imports now let you say Signal.map4 and List.filter with no extra imports, it is closer to how it was pre-0.14, but things are qualified. I wonder to what extent this addresses concern 2?

Joey Eremondi

unread,
Feb 7, 2015, 1:01:54 PM2/7/15
to elm-d...@googlegroups.com

And ::, but if those 3 were covered then it would address most concerns I think.

And you're right, this would reduce boilerplate which would alleviate some concerns. And having 2a would help, especially if Result is the "default" exception method.

Max Goldstein

unread,
Feb 7, 2015, 1:05:00 PM2/7/15
to elm-d...@googlegroups.com
The new exposing keyword means we should probably talk about unqualified imports as exposed, which I think is more intuitive and also has a slight negative connotation, discouraging their use. Qualified imports might just be "module imports".

I'm also trying to figure out how this works with default imports. I assume import Basics exposing (..) will still work, and in this case (importing everything exposed), the module name should not be added to the environment. Otherwise I like the implicit module import. I favor 2b, no default module imports, since that defeats the stated goal of eliminating "where did this come from?".  However, if multiple import statements from the same module are disallowed, it becomes impossible to import Maybe or anything else in the default imports. So I think some special casing is in order, and while you're at it, you may want to disallow the exposing (..) pattern for non-default imports.

FWIW, I'd really like to see Text.asText as an exposed default import. Let me print something to the screen already, thank you. Also, import Graphics.Element exposing (Element) because I should be able to express the type of main. (That is, the compiler knows about Elements so we shouldn't keep them out of the type environment.)

I think import Signal exposing ((..)) has been brought up to import infix operators. 

Rehno Lindeque

unread,
Feb 7, 2015, 1:21:30 PM2/7/15
to elm-d...@googlegroups.com, jo...@eremondi.com
Problem 2 is solved by either having IDE support for automatic imports, having a broader set of things imported (qualified) by default, or having some sort of import-inference.

My personal feeling is that I don't mind long import lists in packages so much. But in large projects, where they can get very tedious I like to have something like my own hand-crafted prelude using re-exports.

E.g.

module Prelude 
  ( module List
  , module List.Extra as List
  , module Array
  , module Array.Extra as Array
  , module Array.Focus as Array
  ) where ...  

...to my mind, in Haskell (perhaps in Elm also), really should have meant that List and Array would be put into my namespace, qualified. Perhaps via some other syntax if necessary, like import Prelude modules or something, I'm not too bothered by the specific syntax.

Evan Czaplicki

unread,
Feb 7, 2015, 1:49:07 PM2/7/15
to elm-d...@googlegroups.com, Joey Eremondi
Joey, (::) is imported by default with 0.14.1, so I think it's just (<~) and (~). I personally avoid these, but I understand that many people like them and use them. I think it's reasonable to consider adding them to the default imports. I'll talk to people in person about this, I think that'll help.

Max, yeah, asText is annoying as is. It's weird that it's in Text and not in Graphics.Element, but there needs to be some juggling to get a module hierarchy without cycles. There's a Text refactor going on, so maybe we can move all functions that return an Element to Graphics.Element?

Max (part 2), the reason that Element is not available by default is that some people never use Elements in their entire application. I expect some non-trivial fraction of professional users are in this camp (Prezi and Richard at least).

Rehno, that is a very interesting idea, I am intrigued! I wonder if we are trying to reinvent C#'s namespaces? I haven't done C# in a while and don't remember the specifics, so I think it'd be cool to split your idea out into a separate thread and gather some information about C# namespaces and maybe the equivalent Java feature (if there is one). It sounds like a professional kind of thing, so I wouldn't be surprised if these languages nailed it more than Haskell :) But yeah, separate thread for that!

Evan Czaplicki

unread,
Feb 7, 2015, 2:37:38 PM2/7/15
to elm-d...@googlegroups.com
Looks like C# namespaces are not relevant. Searching for any language besides Haskell that has something similar. But again, let's start a separate thread if there are any findings!
--
Sent from Gmail Mobile
Message has been deleted

Chris Knox

unread,
Feb 9, 2015, 2:05:02 PM2/9/15
to elm-d...@googlegroups.com, jo...@eremondi.com
I just noticed a confusing (to me as an elm newbie) behaviour related to the default import of the List(::) operator

This compiles

import List
alisttail = List.tail (1 :: 2 :: [])

and this compiles

import List(..)
alisttail = tail (1 :: 2 :: [])

but this did not compile

import List(tail)
alisttail = tail(1 :: 2 :: [])

but this fails because it can't find '::' - it appears that the explicit import of List(tail)  causes List.:: to be not imported even though it is normally imported by default. I suspect that is the sensible behaviour as it would be surprising to make an explicit import and find that the default imports were still in scope, but in this specific case it is a problem as I could not find a way to explicitly import the cons operator.

import List(::)

gives whitespace errors.

Alexey Zlobin

unread,
Feb 10, 2015, 11:03:29 AM2/10/15
to elm-d...@googlegroups.com
What about nested imports?

Given "main : Element" what would import be look like? I mean not intentionally simplified case as in the latest examples but suggested best practice that scale.

--

Richard Feldman

unread,
Feb 17, 2015, 11:47:25 AM2/17/15
to elm-d...@googlegroups.com
These semantics seem awesome! Just a really nice upgrade.

As for syntax, I think we can do better than "exposing" - which feels more like a keyword for exports than for imports.

What about this?

require List
require List importing (map, filter)
require List importing (..)

require List as L
require List as L importing (map, filter)
require List as L importing (..)

This is based on the idea that really what you're doing is importing those functions into the current namespace.

Obviously "import Foo importing (..)" would be awkward, so I swapped "import" for "require" - which separately seems reasonable, given that "import Foo" by itself no longer imports any functions into the current namespace.

Thoughts?

Laurence Roberts

unread,
Feb 17, 2015, 12:49:24 PM2/17/15
to elm-d...@googlegroups.com
Or this?

import List with (map, filter)

Will White

unread,
Aug 19, 2016, 4:37:33 PM8/19/16
to Elm Discuss
I have an idea that I think is nice. Make writing List.map actually do what import List exposing (map) does, so you don't have to write import List at all. And if you want to use a function without a namespace, e.g. Html, import Html could do what import Html exposing (..) does now.

Joey Eremondi

unread,
Aug 19, 2016, 4:41:05 PM8/19/16
to elm-d...@googlegroups.com
I had suggested something similar a while back: https://groups.google.com/forum/#!topic/elm-discuss/qJL51Kf8C2M

The arguments against it are still valid.

I don't think there's  widespread dissatisfaction with the import system as of 0.17, so I doubt it will change any time soon.

--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.

Nick H

unread,
Aug 19, 2016, 5:00:07 PM8/19/16
to elm-d...@googlegroups.com
Will, how would you propose to deal with selective imports? For example:

import Html.Attributes exposing (class)

This is a frequent usage for me. Html.Attributes has a bazillion things in it, and a lot of them are common words. I want to de-namespace "class" because I use it frequently, but not the rest of that junk.


Will White

unread,
Aug 19, 2016, 5:13:20 PM8/19/16
to Elm Discuss
That's optimisation work that should be done by the compiler. The fact that class is only one of an arbitrary number of functions in Html.Attributes isn't actually something the programmer should have to think about. The compiler would look at the Html.Attributes you use in your file, find only class, and do whatever import exposing (class) does now.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.

Will White

unread,
Aug 19, 2016, 5:19:15 PM8/19/16
to Elm Discuss
Just to be clear, this idea has now been prompted by a real problem: https://groups.google.com/d/msg/elm-discuss/-e27G4vwjGY/6N-RErBiBAAJ. Changing the way imports work like this would mean Elm could guarantee that problem couldn't happen, and make imports more pleasant to use to boot. Elm does require that filenames and module names match, so I'm not sure why Evan mentioned that then.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.

Nick H

unread,
Aug 19, 2016, 5:23:03 PM8/19/16
to elm-d...@googlegroups.com
It is something I have to worry about, because having 50+ useless functions with names like "id" and "title" imported into my default namespace is a recipe for naming collisions and confusing compiler errors.

Supposing Html.Attributes had a function called "toString" that I didn't know or care about? Your proposal sounds like it will take the problem you are trying to solve and just make it worse.

To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.

Nick H

unread,
Aug 19, 2016, 6:29:37 PM8/19/16
to elm-d...@googlegroups.com
Let me try to state my concern in a different way. You say:


The fact that class is only one of an arbitrary number of functions in Html.Attributes isn't actually something the programmer should have to think about

I completely agree with this! And it is exactly why I write "exposing (class)" and not "exposing (..)". Because I am telling the compiler "Whatever else is in Html.Attributes, I don't want to think about it. So please leave it in its namespace where it won't get in my way." This approach works whether class is the only function in the module, or if it is one of millions.

Will White

unread,
Aug 20, 2016, 4:07:44 AM8/20/16
to Elm Discuss
Yes, you're right. What I should have said is that it shouldn't be something the programmer has to think about until there's a name collision.

I've been import exposing (..) a lot, and using variable names like id too. I haven't noticed lots of naming collisions as you describe, so I thought I'd check it out:

module Main exposing (..)

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.App exposing (..)

main
=
   
Html.App.beginnerProgram
       
{ model = ()
       
, update = (\_ _ -> ())
       
, view = view
       
}

id
=
   
2

view model
=
    div
[ class "something" ] []


This doesn't actually cause any naming collisions between id and Html.Attributes.id.

Will White

unread,
Aug 20, 2016, 4:48:36 AM8/20/16
to Elm Discuss
But it would cause naming collisions if there were two imported modules both with id. As import exposing (a, b) would break the guarantee against the problem I had, that could be made clear, perhaps by naming it unsafe import exposing (a, b).

Francesco Orsenigo

unread,
Aug 22, 2016, 6:10:38 AM8/22/16
to Elm Discuss
The only issue I have with imports is that any code that contains several unqualified imports is a pain in the ass to learn.

Example: https://github.com/Bogdanp/elm-combine/blob/2.2.1/examples/Python.elm

As a newbie, it's immensely frustrating being unable to easily track which of the `import Whatever exposing (..)` is actually providing a specific symbol.
It should be disallowed entirely.
I don't mind writing more if it makes my code more readable.

Will White

unread,
Aug 22, 2016, 9:08:46 AM8/22/16
to Elm Discuss
I don't think it should be disallowed entirely. Disallowing it would make Html and other familiar things like that harder to read, without helping understanding.

Francesco Orsenigo

unread,
Aug 22, 2016, 9:12:53 AM8/22/16
to elm-d...@googlegroups.com
Not at all.
You'd use qualified imports.

`import Html exposing (div, span, text, input, form)`

or

`import Html as H`
and then `H.div` `H.text` and so on.

One of the strength of Elm is keeping everything very explicit, which
makes it super newbie friendly.
> --
> You received this message because you are subscribed to a topic in the
> Google Groups "Elm Discuss" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/elm-discuss/ydhKsvJtDZw/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to

William Bailey

unread,
Aug 22, 2016, 11:52:21 AM8/22/16
to Elm Discuss
For years I coded in Java using IntelliJ.  It was super easy in that IDE to hover on any symbol, system or otherwise, and learn all about it.  Or hit ctrl-B (eg) to drill into the source. Again, worked fine for system libraries and was really useful for seeing how the under-the-covers stuff worked.

When I started coding in go-lang a couple years ago, people were griping about having to manually remove unused imports as the compiler would not allow that.  But now, most editors have plug-ins that do that automatically.

So... I think focusing on what the core language should and should not allow here is misguided.  Just need better IDE tools.

Francesco Orsenigo

unread,
Aug 22, 2016, 11:56:24 AM8/22/16
to elm-d...@googlegroups.com
Are you going to write such plugin for every possible editor?
Is every newbie going to have such plugin?
What about browsing code from GitHub?

A language's readability should not depend on tooling.

Alejandro Do Nascimento

unread,
Nov 15, 2016, 1:33:21 PM11/15/16
to Elm Discuss
I'm just starting with Elm, I'm as newbie as someone can be. I'm reading some examples of how to use the hop router on github, most of the things I've have found use a lot of exposing (..) so I find myself wasting time just searching the declarations of the symbols I encounter. I think that's the only thing that I have found that makes Elm program a little harder to read.

Maybe you can do like python does, in python you can import everything into your namespace with from module import * but that's something that's kind of frown upon the community and official style guide.

Joey Eremondi

unread,
Nov 15, 2016, 1:55:26 PM11/15/16
to elm-d...@googlegroups.com

I'm not sure what your proposing, the Python syntax you stated does the exact same thing as exposing (..).

Showing where a name came from would be a great IDE feature though, Haskell can do this on Atom and it's really helpful.


You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.

Alejandro Do Nascimiento

unread,
Nov 19, 2016, 5:59:20 PM11/19/16
to elm-d...@googlegroups.com
Just to raise awareness that using this kind of imports can difficult for readability, that maybe it should not be removed from the language but discourage by the community.

Just following the python example, from PEP8 the python style guides:

"Wildcard imports ( from <module> import * ) should be avoided, as they make it unclear which names are present in the namespace, confusing both readers and many automated tools. There is one defensible use case for a wildcard import, which is to republish an internal interface as part of a public API (for example, overwriting a pure Python implementation of an interface with the definitions from an optional accelerator module and exactly which definitions will be overwritten isn't known in advance)."

Also, depending on an IDE or external tools for me it's a sign that there is a problem. As others have said, what about github and who's going to maintain the plugins for all the IDEs, maybe one day you'll be through ssh with the system's default vim.

It's just my perspective, cheers.

To unsubscribe from this group and all its topics, send an email to elm-discuss+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
AD.N
Reply all
Reply to author
Forward
0 new messages