
I often wish to be able to define local types and instances, so, for example: import Data.Set as Set ordNubBy :: (a -> a -> Ordering) -> [a] -> [a] ordNubBy cmp = go Set.empty where newtype T = T a instance Ord T where T x `compare` T y = cmp x y go _ [] = [] go ys (x:xs) = bool (x:) id (T x ∈ ys) $ go (Set.insert (T x) ys) xs The notion is that type and instance declarations would become legal in `let` or `where` bonds; they would effectively declare new types and instances in that scope for each use of that term; and such local types and instances could never leave the scope they were defined in, by enforcement, so consistency of instances would be preserved. I know some related work which proposes local instances [0] but none i know proposes also local types. I'm seeking some feedback here before i formalize further and potentially start making implements. Thoughts? Questions? Better ideas? Critical flaws? [0] http://okmij.org/ftp/Haskell/TypeClass.html#local

local types and instances
is this not an attempt to move module functionality inside function? would this not encourage larger functions? are larger functions a good thing? could this not be better addressed by allowing explicit export / hiding of instances in a module? does not type declaration inside function come at a performance cost?

Languages should express our intent, and giving type and instance
declarations the ability to be localised will enable them to serve as
better vehicles of expression.
That said,
Imants Cekusins
local types and instances
is this not an attempt to move module functionality inside function?
Same counter-argument can be applied to local functions.
would this not encourage larger functions? are larger functions a good thing?
Same as above.
could this not be better addressed by allowing explicit export / hiding of instances in a module?
If I interpret the intent correctly, this is specifically about finer granularity than module level.
does not type declaration inside function come at a performance cost?
Generally speaking, and whether this suggested cost is real, Haskell programmers are more or less used to pay the cost of abstraction. -- с уважениeм / respectfully, Косырев Сергей

is this not an attempt to move module functionality inside function? Same counter-argument can be applied to local functions.
not quite. Let's look at the current state: Module inside module: ok (module reexport) Type inside type: ok (data declaration etc) Function inside function: ok (local functions) Type / Instance / module inside function is quite a bit different
finer granularity than module level. by turning function into a mini module?
Haskell programmers are more or less used to pay the cost of abstraction. well should we not start thinking about keeping those costs down if
Currently module does not give control over instance export. Should this not be addressed first? This would be a step towards more complexity in syntax. While there may be benefits, complexity always comes at a price. possible - performance and otherwise? Ideally, minimizing them?

Languages should express our intent
Vocabulary of a certain size is necessary to write good books, or read them. Quality or popularity of those good books is however not measured by the size of vocabulary. Language richness is only a part of the story. Language use is just as important.

On Mon, 2016-01-25 at 00:11 -0800, M Farkas-Dyck wrote:
I often wish to be able to define local types and instances, so, for example:
import Data.Set as Set
ordNubBy :: (a -> a -> Ordering) -> [a] -> [a] ordNubBy cmp = go Set.empty where newtype T = T a
instance Ord T where T x `compare` T y = cmp x y
go _ [] = [] go ys (x:xs) = bool (x:) id (T x ∈ ys) $ go (Set.insert (T x) ys) xs
I like local types a lot. I'm not sure how your example works w.r.t. type variable scopes. Are you assuming ScopedTypeVariables? It will probably require explicit quantification to work: ordNubBy :: forall a. (a -> a -> Ordering) -> [a] -> [a] Without ScopedTypeVariables `a` in `newtype T = T a` is different from the one in top level type signature, is it? At least that is how type variables work in local function signatures. In general, I see increasing demand for change scoping in haskell. For example see https://ghc.haskell.org/trac/ghc/wiki/Proposal/OpenImportExtension https://ghc.haskell.org/trac/ghc/wiki/Records/NestedModules https://ghc.haskell.org/trac/ghc/ticket/10478 http://blog.haskell-exists.com/yuras/posts/namespaces-modules-qualified-impo... Probably all of that should be addressed in a complex.
The notion is that type and instance declarations would become legal in `let` or `where` bonds; they would effectively declare new types and instances in that scope for each use of that term; and such local types and instances could never leave the scope they were defined in, by enforcement, so consistency of instances would be preserved.
I know some related work which proposes local instances [0] but none i know proposes also local types.
I'm seeking some feedback here before i formalize further and potentially start making implements. Thoughts? Questions? Better ideas? Critical flaws?
[0] http://okmij.org/ftp/Haskell/TypeClass.html#local _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On 25/01/2016, Imants Cekusins
could this not be better addressed by allowing explicit export / hiding of instances in a module?
That wouldn't enable such definitions as mine earlier, where the instance is defined in terms of other local terms.
does not type declaration inside function come at a performance cost?
Newtype declaration doesn't as they are elided at build time, and (at
least with GHC) the program would merely pass a class dictionary with
the locally-defined methods.
On 25/01/2016, Imants Cekusins
Currently module does not give control over instance export. Should this not be addressed first?
To my knowledge this would be a breach of consistency of instances:
one could so define terms with instances in conflict [0].
[0] http://stackoverflow.com/questions/8728596/explicitly-import-instances
On 25/01/2016, Yuras Shumovich
I'm not sure how your example works w.r.t. type variable scopes. Are you assuming ScopedTypeVariables? It will probably require explicit quantification to work:
ordNubBy :: forall a. (a -> a -> Ordering) -> [a] -> [a]
Yes, indeed, thanks for catching that ☺
In general, I see increasing demand for change scoping in haskell. For example see
...
Probably all of that should be addressed in a complex.
I think my proposal is orthogonal to all those as none of them seems to allow local instances defined in terms of other local terms.

That wouldn't enable such definitions as mine earlier, where the instance is defined in terms of other local terms.
agree - that wouldn't. However tweaks over instance export would allow you to move a complex function to its own module where module-local instances could be defined.
this would be a breach of consistency of instances: one could so define terms with instances in conflict [0].
could GHC (with appropriate changes) figure conflict out and throw error? e.g. GHC could enforce that instances can only be hidden if they are not referred to in any of exported types or functions in the instance defining module? .. it could cause error to import the same instance-defining module (including imports by imports) with and without instances hidden. .. mark instance (with a pragma?) as module-local .. only most basic instances (which compile without pragmas) could be hidden .. hide / export all or no instances (for a given class?) within a module .. another safety net? personally, I am happy with the current GHC as it is. Just wondering: is function - the appropriate place to define types and instances.

.. another safety net: module-local instance could be defined only for module-local (non-exported) types. Would this not offer what you are looking for, without breaking instance consistency?
participants (4)
-
Imants Cekusins
-
Kosyrev Serge
-
M Farkas-Dyck
-
Yuras Shumovich