Is it possible to have sub-modules in Haskell? - haskell

I have this code
module M where
a = 1
b = 2
x = 10
y = 20
...but as the module grows it's difficult to deal with duplicate names.
Is it possible to have namespaces like this?
module M where
module A where
a = 1
b = 2
module X where
x = 10
y = 20
..and then
...
import M
s = A.a + X.y

What you proposed isn't currently supported in Haskell AFAIK. That said, nothing's stopping you from creating seemingly namespaced modules. For example:
module Top where
myTopFunc :: a -> Int
myTopFunc _ = 1
and in another file:
module Top.Sub where
mySubFunc :: a -> String
mySubFunc _ = "Haskell"
In addition to this, you have a few more tricks up your sleeves to arrange your modules. If you import a module A into B, you can export A's visible entities from B as if they were its own. Subsequently, on importing B, you'll be able to use those functions/datatypes etc. being oblivious to where they originally came from. An example of this using the modules from above would be:
module Top (
myTopFunc,
TS.mySubFunc
) where
import qualified Top.Sub as TS
myTopFunc :: a -> Int
myTopFunc _ = 1
Now you can use both functions just by importing Top.
import Top (myTopFunc, mySubFunc)

There are hierarchical module names. You can have modules named M and M.A and M.X, but modules themselves don't nest, and M is unrelated to M.A as far as the language is concerned.
If you want M to export everything M.A and M.X export, you have to do this explicitly:
module M (module M.A, module M.X) where
import M.A
import M.X
-- rest of M

No you cannot. But your comment mentions you're just looking for a way to avoid naming collisions. For this, you can use the {-# LANGUAGE DuplicateRecordFields #-} extension and the compiler will allow duplicate record field names.

Related

Updating a record using RecordDotSyntax results in an error

Context
If I load the following into ghc-9.2.1-alpha2 which has support for RecordDotSyntax:
{-# LANGUAGE OverloadedRecordDot, OverloadedRecordUpdate, DuplicateRecordFields #-}
----------------------------------------------------------------------
data Point = Point { x :: Double, y :: Double }
instance Show Point where
show p = "Point { x = " ++ show p.x ++ ", y = " ++ show p.y ++ " }"
p = Point 10 20
I can then run the following in ghci:
ghci> p { x = 30 }
Point { x = 30.0, y = 20.0 }
Cool, it's working!
Issue
However, if I add the following to my test file above:
result =
let
a = Point 1 2
b = a { x = 3 }
in
b
and reload, I get the following message:
ghci> :r
[1 of 1] Compiling Main ( /home/dharmatech/tmp/test-ghc-9.2.0.20210422/point-update-issue.hs, interpreted )
/home/dharmatech/tmp/test-ghc-9.2.0.20210422/point-update-issue.hs:13:13: error:
RebindableSyntax is required if OverloadedRecordUpdate is enabled.
|
13 | b = a { x = 3 }
| ^^^^^^^^^^^
Failed, no modules loaded.
What I've tried
If I add RebindableSyntax as the message suggests, I get many more errors which look like this:
/home/dharmatech/tmp/test-ghc-9.2.0.20210422/point-update-issue.hs:3:27: error:
Not in scope: type constructor or class ‘Double’
|
3 | data Point = Point { x :: Double, y :: Double }
|
Question
Is there a way to get this to work? Or is it something that's just not implemented yet?
Update 2021-08-10
If I add the following as alias and Ari suggested:
import Prelude
import GHC.Records
I get the following:
point-update-issue.hs:17:13: error:
Not in scope: ‘setField’
Perhaps you meant ‘getField’ (imported from GHC.Records)
|
17 | b = a { x = 3 }
| ^^^^^^^^^^^
Failed, no modules loaded.
There are two problems here.
You have enabled RebindableSyntax. This allows you to redefine certain behaviour
by defining certain functions, otherwise imported from Prelude, in the local scope. Therefore, RebindableSyntax implies NoImplicitPrelude. You need to manually import Prelude, optionally hiding functions you want to override.
import Prelude hiding (...)
You have enabled OverloadedRecordUpdate, an experimental feature from GHC 9.2 that has not yet been stabilized.
The relevant version of the GHC User's Guide has this to say:
At this time, RebindableSyntax must be enabled when OverloadedRecordUpdate is and users are required to provide definitions for getField and setField. We anticipate this restriction to be lifted in a future release of GHC with builtin support for setField.
The default getField can be imported from GHC.Records, but setField is not currently available, so you have to implement it yourself.
Have fun!
EDIT 11/08: This answer is mostly incomplete and partly incorrect. See my other answer.
As #alias explained, if you enable RebindableSyntax, the Prelude will not be automatically loaded. Nor will GHC.Records, normally loaded by and required by record syntax extensions.
You'll have to add this:
import Prelude
import GHC.GetRecords

Module, that exports another ones

Is there a way to create a module that will export other modules?
For example, I have a list of modules: A, B, C. And I want them to be imported to module D.
So, I have to write:
import A
import B
import C
It works. But may be not very convenient sometimes.
Is there a way to create a Collection module that exports the content of A, B and C?
With this feature, instead of previous instructions, I'd only have to write:
import Collection -- Importing A, B, C.
Yes, but you need to use an explicit export list, specifying all functions, types, classes and modules to export from this module.
module Foo (module A, module B, myid) where
import A
import B
myid :: a -> a -- For example

Haskell export current module with additional imported module

Is it possible to write a module in Haskell, which re-exports a module in addition to exporting everything visible inside?
Lets consider following module:
module Test where
import A
f x = x
This module exports everything defined inside, so it exports f but does not re-export anything imported from A.
On the other hand, if I want to re-export the module A:
module Test (
module A,
f
) where
import A
f x = x
Is there a way to re-export A and export everything defined in Test without needing to explicitly write every function defined within Test?
There is a simple solution, just export the module from the module:
module Test
( module Test
, module A
) where
import Prelude()
import A
f x = x

Numeric.Probability.Monad not exported from probability package and therefore examples don't work?

I was playing around with the probability package, trying to understand how the various examples work. A number of the examples import Numeric.Probability.Monad which is hidden it seems and therefore means I can't run the examples.
Monty Hall Example:
module Numeric.Probability.Example.MontyHall where
import qualified Numeric.Probability.Distribution as Dist
import qualified Numeric.Probability.Transition as Trans
import Numeric.Probability.Simulation ((~.), )
import Numeric.Probability.Percentage
(Dist, RDist, Trans, )
import qualified Numeric.Probability.Monad as MonadExt
And If i try to run it in ghci
:load "MontyHall.hs"
MontyHall.hs:10:18:
Could not find module `Numeric.Probability.Monad'
it is a hidden module in the package `probability-0.2.4'
Use -v to see a list of the files searched for.
Failed, modules loaded: none.
Clearly I am doing something wrong, as what is the point of examples that can't be run. So what I am doing wrong here?
The only function from Numeric.Probability.Monad used in the MontyHall file is this one:
compose :: Monad m => [a -> m a] -> a -> m a
compose = foldl (flip (<=<)) return
It's a straightforward helper function, and you can just inline it yourself.

Re-export qualified?

suppose you have two modules like
module Foo.A where
foo = 42
and
module Foo.B where
foo = 12
and you want to write a super module
module Foo (
module Foo.A
, module Foo.B
) where
import Foo.A
import Foo.B
which re-exports those modules, you would get a name clash.
Is there a solution for this?
Basically, no. This has been a long-standing feature request by people like the authors of Gtk2hs. Gtk2hs has a very broad module hierarchy where it might make sense to both:
Use the same name in several different modules (e.g. newButton in both Graphics.UI.Gtk.Buttons.Button and Graphics.UI.Gtk.Buttons.CheckButton)
Provide the convenience to the user of importing all these modules with a single import statement
For now, if you want to reexport several modules together all you can do is:
Avoid reusing names in the modules you wish to reexport
Where appropriate, use type classes to allow the same name to be used for several different purposes
Good question. The Haskell Report addresses this:
Exports lists are cumulative: the set of entities exported by an export list is the union of the entities exported by the individual items of the list.
[...]
The unqualified names of the entities exported by a module must all be distinct (within their respective namespace).
According to my limited Haskell knowledge I'd say it's not possible.
You can't export both foos without the name clash unless you use a typeclass (which I shall elaborate on), but there are other options.
You could opt to hide one of the versions of foo:
module Foo
(
module Foo.A,
module Foo.B
)
where
import Foo.A
import Foo.B hiding (foo)
This isn't ideal if you genuinely need both, but if one is rarerly used then it may be simpler to just hide it and expect people to manually reinclude (e.g. by import qualified Foo.B as B (foo), providing B.foo) it if they need it.
You could opt to hide both versions of foo and document that you expect the end user to manually import them if they need them. Under such a scheme, the user could do something like the following:
import Foo.A as A (foo)
import Foo.B as B (foo)
main :: IO ()
main = do
print A.foo
print B.foo
This is actually a fairly common thing to have to do when dealing with multiple container types since many different container types export functions like empty, null and singleton. (To the point where I wish there were actually typeclasses for those, since they are such common operations.)
If you really need both and you're prepared to do a bit of export gymnasics, you can reexport both under different names from within Foo itself:
module Foo (module Foo) where
-- Note that in this case it is important
-- that the module alias matches the
-- name of the exporting module ('Foo')
import Foo.A as Foo hiding (foo)
import Foo.B as Foo hiding (foo)
import qualified Foo.A
import qualified Foo.B
fooA = Foo.A.foo
fooB = Foo.B.foo
Thus any module importing Foo will get Foo.A.foo as fooA (with a value of 42) and Foo.B.foo as fooB (with a value of 12).
If you only have a few clashes to resolve then this is probably the most balanced solution
As another answer has already mentioned, you can use a typeclass, though depending on the context this may be a misuse of the typeclass system.
You could achieve that like so:
module Foo.C where
-- The 'a' dummy argument is necessary to disambiguate
-- which version of 'foo' you intend to use.
class FooProvider a where
foo :: a -> Int
module Foo.B where
import Foo.C
data FooB = FooB
instance FooProvider FooB where
foo _ = 12
module Foo.A where
import Foo.C
data FooA = FooA
instance FooProvider FooA where
foo _ = 42
module Foo (module Exports) where
-- In this case the name of the
-- module alias is unimportant
-- as long as all modules use the same alias.
import Foo.A as Exports
import Foo.B as Exports
import Foo.C as Exports
import Foo
-- 'FooA' and 'FooB' exist solely for
-- the purpose of disambiguating the
-- different versions of 'foo'.
main :: IO ()
main = do
print (foo FooA)
print (foo FooB)
Generally this is less desirable than the aforementioned solutions, particularly for something as simple as a named constant, but sometimes it makes sense to introduce a typeclass. For example, a typeclass such as this:
class Container c where
empty :: c a
singleton :: a -> c a
null :: c a -> Bool
Would work for a number of container types, including [] ('List'), Set, Maybe. Without singleton, it would apply to even more types (e.g. Map, Seq, IntMap). (Note that this could also have included some kind of length function since an empty container would have a length of 0 and a singleton container would have a length of 1. It would however clash with the length in Prelude, which is based on Foldable.)
There may be other possibilities that make use of typeclasses and possibly compiler extensions, but I suspect they'll only increase in complexity from herein.

Resources