How to re-export values and types from a module?


#1

How can you re-export things from a module?

For example, let’s say I want to make some functions that deal with dates. I depend on Time.Month, and define my own type for Year and Day. I want to export Month, Year, and Day from the component.

module Dates exposing (Day, Year, Month)

import Time exposing (..)

type alias Day = Int
type alias Year = Int

However, I get an error: You are trying to expose a type named Month but I cannot find its definition.

How do you do this? (Also, I swear this used to be possible).


#2

Well, you only defined Day and Year, didn’t you?
You have to redefine Month as an alias to Time.Month.


#3

What if Month has constructors, and I want to export those as well? (See Time.Month, it has Jan | Feb | Mar | ...)


#4

I see, your only options I know of are:

  • Ask your users to import Time and use its Month type if needed
  • Create a custom type identical to Time.Month and map one to the other each time you use it
  • Create exposed constructors functions for each value, like jan = Time.Jan, but your users won’t be able to pattern match

I would love a way to export an alias to a custom type with all its constructors, as it is also currently hard to organize a package with internal modules and a single exposed module. Something like type alias Month = Time.Month(..).


#5

Hiding Time.Month behind a custom type wouldn’t actually solve any package version conflicts though, would it? I don’t believe Elm allows you to have two different versions of an indirect dependency in a project, even if those indirect dependencies are entirely “hidden” inside direct dependencies.

I think the right answer is just to mention in the docs that the Month type comes from elm/time, and that users should import Time if pattern matching is needed. A bit more code and is perhaps sightly less pretty at first glance, but reduces indirection and makes everything very explicit and transparent.


#6

It wouldn’t. And it’s a little off-topic, so I removed the note from my answer. Thanks.


#7

Here’s an example of a date library that does exactly that:

https://package.elm-lang.org/packages/justinmimbs/date/latest/Date#Month


#8

This is really impossible in Elm? I’m surprised, but it must be a deliberate decision. In Haskell, it would be done like this: (Transliterated to Elm code)

module Dates exposing (Day, Year, Month(..))

import Time exposing (..)

type alias Day = Int
type alias Year = Int

Not having this feature prevents you from modularizing your code the way you might want. For example, if my Date module gets too big, I can’t define Day, Year, and Month in different submodules, because then it would be impossible to export them in the same place!

Does anyone have any context as to why Evan decided not to support this feature?


#10

One scenario to consider is this:

Let’s say I publish a package zwilias/dependency. It exposes a module Dependency which looks like this:

module Dependency exposing (MyCustomType(..))

type MyCustomType = Ctor1 | Ctor2

Some time later, I publish a new (major) version of this package, where I add another constructor:

module Dependency exposing (MyCustomType(..))

type MyCustomType = Ctor1 | Ctor2 | Ctor3

I also create a package which re-exposes the MyCustomType type, and which doesn’t really care about which constructors are actually available. So in my elm.json, I have something like this:

"dependencies": { "zwilias/dependency": "1.0.0 <= v < 3.0.0" }

Now, if I go and look at the docs of this package, which constructors are shown? If I install this package, which constructors can I use?


The core issue with re-exposing an imported type with constructors, is that what you expose depends on what your user has installed.


closed #11

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.