Dealing with ambiguous occurrence in Haskell - haskell

I'm importing both Data.Text and Data.List and their respective find functions collapse.
import Data.Text
import Data.List
my_list = [4, 2, 4, 5, 6, 2, 6]
find (\a -> a == 5) my_list
The above code leads to the following error
Ambiguous occurrence ‘find’
It could refer to
either ‘Data.Text.find’, imported from ‘Data.Text’
or ‘Data.List.find’,
imported from ‘Data.List’
(and originally defined in ‘Data.Foldable’)
Is the only solution to use Data.List.find (\a -> a == 5) my_list here, or qualified imports?
What strikes me is that
Data.List.find (\a -> a == 5) my_list
--> Just 5
Data.Text.find (\a -> a == 5) my_list
--> error: Couldn't match expected type ‘Text’ with actual type ‘[Integer]’
From the find function signatures, the compiler is apparently able to understand that Data.Text.find doesn't work with [Integer]. Can't he use this information to decide on which instance of find to use and, in this case, automatically use find from Data.List ?

Is the only solution to use Data.List.find (\a -> a == 5) my_list here, or qualified imports?
As far as I know, yes, there are no other solutions.
The compiler won't try to type-check both options and use the one which makes your code compile. In the most general case, that could lead to an exponential blow-up. Consider, e.g.,
foo a1 a2 .... aN
where each one of a1 .. aN are imported from two distinct modules. Trying which combination type-checks would in principle require to test 2^N combinations.
Further, there's always the chance that the programmer meant to use an identifier from a module, but they made a mistake and the code does not type-check. In this case, the same identifier from the other module might instead type-check, making the code compile but producing the wrong result. (This is a rather contrived example, I know.)
Note that, if in your importing module you only need to use one of the two finds you can hide the other:
import Data.Text hiding (find)
import Data.List
Alternatively, you can provide explicit disjoint import lists:
import Data.Text (pack)
import Data.List (find, zip)
If you need both finds, you can provide a shorter name for the module
import Data.Text as T
import Data.List as L
as then disambiguate using L.find. You won't need the shorter module name except for the doubly imported identifiers. If you use import qualified instead you will need to specify the module name for all imported identifiers.

Related

haskell hide import of star-operator

I have:
import qualified GHC.Types as T hiding (Module, (*))
import GHC.TypeNats hiding ((*))
but when I try to define a (*)-operator, it fails:
{-# INLINE (*) #-}
infixl 7 *
(*) :: r -> r -> r
(*) = undefined
with
error:
Ambiguous occurrence ‘*’
It could refer to either ‘T.*’,
imported qualified from ‘GHC.Types’ at ...
or ‘*’,
imported from ‘GHC.TypeNats’ at ...
|
1277 | infixl 7 *
EDIT: the description is currently not reproducible. I will update the question as soon as I can.
Neither GHC.Types nor GHC.TypeNats have a (*) export. They both have a type (*) export. Usually, you can distinguish between term-level (*) and type-level (*) by context, but this is not true in export/import lists. Instead, term-level is taken as default, and you must explicitly say you want to hide the types.
import qualified GHC.Types as T hiding (Module, type (*))
import GHC.TypeNats hiding (type (*))
The reason Module remains unadorned is because the capitalization means it must be either a type, a constructor, or a pattern synonym. Constructors must occur inside grouping () next to their data types, and pattern synonyms must be set apart with pattern, so the Module above is taken to refer to the data type (hiding the data type also hides all of its constructors).
The reason you did not get an error when you tried to hide something that didn't exist is because hiding was designed so that, if the imported module ever stopped exporting something you hid, your module would not fail to compile. After all, the only reason you referenced that thing was to say that you weren't going to reference that thing.

Haskell - Type incompatibility after qualified import

I'm using hMatrix and the first lines of my code are:
import Numeric.LinearAlgebra
import qualified Data.Vector as V
The problem is that in my code the vector type is V.Vector, but some methods defined by hMatrix have type Vector -> ... and GHC does not understand when I try to use these methods, saying that they are not defined for the type V.Vector. How can I solve this problem?
Update:
From the documentation of hMatrix:
The Vector type is a Storable vector from Roman Leshchinskiy’s vector package, so all array processing tools provided by this library are directly available.
However, some basic operators like (++) (which is present in Data.Vector.Storable) are not included in hMatrix. Is it impossible to use these from hMatrix or is there some simple way to tell the compiler that these types are the same?
hmatrix uses its own Data.Packed.Vector type and it's different from Data.Vector.
Either using Data.Packed.Vector in your code, or converting Data.Vector to Data.Packed.Vector before calling functions would work.
You could add an explicit import for the Vector type:
import Numeric.LinearAlgebra
import qualified Data.Vector as V
import Data.Vector (Vector)
Though, I didn't know external modules could break depending on how you import modules they depend on.
Note that you can't break hmatrix just because you import another module. You just have a type mismatch as vector provides different types of vectors, including a .Generic interface that works for all of those.
You probably have something along the lines of
import Data.Vector
f :: Vector Int -> Vector Int
f = whatever
If you import Data.Vector.Generic you can write functions that work for all vector types, including those used by hmatrix.
import Data.Vector.Generic
f :: Vector Int -> Vector Int
f xs = xs ++ empty
should work with hmatrix vectors.

Create HashTable in Haskell

I want to create a HashTable in Haskell, insert hash values inside and look up in this HashTable.
I found this documentation but I just started Haskell and therefore I don't really know how to ue these functions.
If some of you could show me some lines of code it would be perfect.
I second Ingo's comment about starting with something simpler. However, I'll break down a few things in a bit of detail.
First of all, I assume you've installed the latest Haskell Platform. In the website for the Platform there is a page with collected documentation for the libraries included with it. Any library that's not in that page would be something you'd need to install separately.
The Platform does include Data.HashTable, so you don't need to install anything, but if you look at the latest Platform's documentation on it, you'll see that it's deprecated and going to be removed soon. So I would not use that module.
The Haskell Platform comes with the two most popular Haskell implementations of a map/dictionary data structure:
Data.Map. (Most of the documentation for this is in Data.Map.Lazy.) This implements a map as a kind of balanced search tree, which means that the keys need to be an ordered type—a type that implements the Ord class. A lot of the built-in Haskell types already implement this class, so this would probably be your easiest choice at first.
The Data.HashMap module hierarchy, with two variants; Data.HashMap.Lazy would be a good starting point. This implements maps as a kind of hash table, so the keys need to implement the Hashable class. This class is newer and not as popular as Ord, so often you might need to implement this class for your key types.
So Data.Map is the easier type to use. But to use it effectively you're going to need to understand a few things beside the most basic language constructs:
How to import a module in a source file.
How to use qualified imports—Data.Map has function names that collide with many of the built-in ones in Haskell, which requires some special syntax.
How to load a module into the ghci interpreter.
How to compile a project that uses the containers library where Data.Map lives (using the cabal tool).
Once you have that down, the easiest way to build a map is from a list of key/value pairs:
module MyModule where
import Data.Map (Map) -- This just imports the type name
import qualified Data.Map as Map -- Imports everything else, but with names
-- prefixed with "Map." (with the period).
-- Example: make a Map from a key/value pair
ages :: Map String Integer
ages = Map.fromList [("Joe", 35), ("Mary", 37), ("Irma", 16)]
A few examples on how to use maps:
-- Example: look up somebody and return a message saying what their age is.
-- 'Nothing' means that the map didn't have the key.
findAge :: String -> String
findAge name = case Map.lookup name ages of
Nothing -> "I don't know the age of " ++ name ++ "."
Just age -> name ++ " is " ++ show age ++ " years old."
-- Example: make a map with one extra entry compared to `ages` above.
moreAges :: Map String Integer
moreAges = Map.insert "Steve" 23 ages
-- Example: union of two maps.
evenMoreAges :: Map String Integer
evenMoreAges = Map.union moreAges anotherMap
where anotherMap = Map.fromList [("Metuselah", 111), ("Anuq", 3)]
As a complement to Ingo's answer, consider using the purely function Data.Map.
import qualified Data.Map as M
myMap :: M.Map Int String
myMap = M.fromList $ zip [1..10] ['a'..'j']
insertedMap :: M.Map Int String
insertedMap = M.insert 11 "fizzbuzz" oldMap
at11 :: Maybe String
at11 = M.lookup 11 insertedMap
Then you can use M.lookup, M.insert, and many other functions to modify/query the map. This datastructure is also purely functional/persistant (notice how IO is nowhere in the types). That means that we can do something like
let newMap = M.insert key val oldMap
in M.union oldMap otherMap
See how we can still use the older version of the map even after inserting something? That's "persistance", we never destroy the older versions of our data structure.
Just so to avoid someone calling the haskell community arrogant, here is a short break down of the first function you'll need:
new :: (key -> key -> Bool) -> (key -> Int32) -> IO (HashTable key val)
This tells us the following: to create a HashTable for a specific key type key you need to pass a function that checks equality on keys, and a function that computes a hash value for keys. So, if eq and hashit would be the desired functions, the following:
new eq hashit
gives you an empty HashTable in the IO-Monad.
An easier way could be to create a HashTable from a list using one of the predefined hash functions:
fromList hashInt [(42, "forty-two"), (0, "zero")]

Explicitly import instances

How do I explicitly import typeclass instances? Also, how do I do this with a qualified import?
Currently, I'm doing
import Control.Monad.Error ()
to import the monad instance that I can use for (Either String). Previously, I used
import Control.Monad.Error
I'm not satisfied with either one, because the Monad instance is implicitly imported.
The inability to control imports of instances is one of the trade-offs the Haskell typeclass system makes. Here's an example in a hypothetical Haskell dialect where you can:
Foo.hs:
module Foo where
data Foo = FooA | FooB deriving (Eq, Ord)
Bar.hs:
module Bar (myMap) where
import Data.Map (Map)
import qualified Data.Map as Map
import Foo
myMap :: Map Foo Int
myMap = Map.singleton FooA 42
Baz.hs:
module Baz where
import Data.Map (Map)
import qualified Data.Map as Map
import Foo hiding (instance Ord Foo)
import Bar (myMap)
instance Ord Foo where
FooA > FooB = True
FooB > FooA = False
ouch :: Map Foo Int
ouch = Map.insert FooB 42 myMap
Yikes! The set myMap was created with the proper instance Ord Foo, but it's being combined with a map created with a different, contradictory instance.
Being able to do this would violate Haskell's open world assumption. Unfortunately, I don't know of a good, centralised resource for learning about it. This section of RWH might be helpful (I searched for "haskell open world assumption").
You can't. Instances are always implicitly exported and hence you can't explicitly import them. By the way, Either e's Monad instance is nowadays in Control.Monad.Instances.
Although the generally correct answer would be "no, you can't", I suggest this horrendous solution:
copy + paste
Take a look at the library source code for the desired module, and copy/paste the necessary data declarations, imports, and function definitions into your own code. Don't copy the instances you don't want.
Depending on the problem at hand, the ghc type system extensions OverlappingInstances or IncoherentInstances might be an alternate solution, though this probably won't solve any problems with the base libraries.

Can I import a non-abstract type abstractly?

Suppose I have a module that contains a type:
module My where
data L a = Nil | Cons a (L a)
The module exports the concrete definition of My to allow for pattern matching etc.
Is it possible for another module, say Client to import My in a manner where L a
is abstract i.e. such that pattern matching on L a values is prohibited by the type-checker?
Yes; you just have to use the import list:
module Client where
import My (L)
ok :: L Int
ok = undefined
bad :: L Int
bad = Cons 3 Nil
bad2 :: L Int -> Int
bad2 (Cons i _) = i
bad2 Nil = 0
If you try to compile this, you'll get the following four errors:
Client.hs:8:10: Not in scope: data constructor `Cons'
Client.hs:8:17: Not in scope: data constructor `Nil'
Client.hs:11:10: Not in scope: data constructor `Cons'
Client.hs:12:9: Not in scope: data constructor `Nil'
If you did want to import the constructors, you would instead specify L(..), or L(Cons) to import Cons but not Nil.
For some other ways you can use the import statement, check out the HaskellWiki article on import (although the article doesn't mention importing data types and their constructors).
Yes, you may do
import My(L())
to import a type without importing any of its constructors. If you still wish to construct values of this type (but not pattern match), you must import functions for doing that construction (for example, by exporting such functions from My or by creating a utility module with such functions).
edit: Since you explicitly mentioned that you wanted a type-checking error, I should point out for completeness that this won't cause pattern-matching on Nil and Cons to be a type-checking error, but merely a scoping error.

Resources