I am trying to load a file.
I have:
wf :: STMutable a File
wf = File.new "worlds/seed_77.world"
data PickleSerialization = pure native com.github.lands.PickleSerialization where
native loadWorld com.github.lands.PickleSerialization.loadWorld :: MutableIO File -> IO World throws IOException, IncorrectFileException
If I try to do:
PickleSerialization.loadWorld wf
I get this error, which seems very confusing to me:
[ERROR: 4]: type error in expression wf
type is IOMutable File
used as MutableIO File
wf returns an action that produces a file. loadWorld takes a file, not an action. I think this should work: wf >>= loadWorld.
MutableIO File represents a mutable file whereas IOMutable is an action that returns a mutable file. IOMutable is defined as (taken from the source):
--- This is an abbreviation for #ST RealWorld (Mutable RealWorld d)#
type IOMutable d = IO (MutableIO d)
Similarly STMutable is defined as,
--- The type of 'ST' actions that return a mutable value of type _d_
--- This is an abbreviation for #ST s (Mutable s d)#
type STMutable s d = ST s (Mutable s d)
Related
I'm still trying to figure out how to split code when using mirage and it's myriad of first class modules.
I've put everything I need in a big ugly Context module, to avoid having to pass ten modules to all my functions, one is pain enough.
I have a function to receive commands over tcp :
let recvCmds (type a) (module Ctx : Context with type chan = a) nodeid chan = ...
After hours of trial and errors, I figured out that I needed to add (type a) and the "explicit" type chan = a to make it work. Looks ugly, but it compiles.
But if I want to make that function recursive :
let rec recvCmds (type a) (module Ctx : Context with type chan = a) nodeid chan =
Ctx.readMsg chan >>= fun res ->
... more stuff ...
|> OtherModule.getStorageForId (module Ctx)
... more stuff ...
recvCmds (module Ctx) nodeid chan
I pass the module twice, the first time no problem but
I get an error on the recursion line :
The signature for this packaged module couldn't be inferred.
and if I try to specify the signature I get
This expression has type a but an expression was expected of type 'a
The type constructor a would escape its scope
And it seems like I can't use the whole (type chan = a) thing.
If someone could explain what is going on, and ideally a way to work around it, it'd be great.
I could just use a while of course, but I'd rather finally understand these damn modules. Thanks !
The pratical answer is that recursive functions should universally quantify their locally abstract types with let rec f: type a. .... = fun ... .
More precisely, your example can be simplified to
module type T = sig type t end
let rec f (type a) (m: (module T with type t = a)) = f m
which yield the same error as yours:
Error: This expression has type (module T with type t = a)
but an expression was expected of type 'a
The type constructor a would escape its scope
This error can be fixed with an explicit forall quantification: this can be done with
the short-hand notation (for universally quantified locally abstract type):
let rec f: type a. (module T with type t = a) -> 'never = fun m -> f m
The reason behind this behavior is that locally abstract type should not escape
the scope of the function that introduced them. For instance, this code
let ext_store = ref None
let store x = ext_store := Some x
let f (type a) (x:a) = store x
should visibly fail because it tries to store a value of type a, which is a non-sensical type outside of the body of f.
By consequence, values with a locally abstract type can only be used by polymorphic function. For instance, this example
let id x = x
let f (x:a) : a = id x
is fine because id x works for any x.
The problem with a function like
let rec f (type a) (m: (module T with type t = a)) = f m
is then that the type of f is not yet generalized inside its body, because type generalization in ML happens at let definition. The fix is therefore to explicitly tell to the compiler that f is polymorphic in its argument:
let rec f: 'a. (module T with type t = 'a) -> 'never =
fun (type a) (m:(module T with type t = a)) -> f m
Here, 'a. ... is an universal quantification that should read forall 'a. ....
This first line tells to the compiler that the function f is polymorphic in its first argument, whereas the second line explicitly introduces the locally abstract type a to refine the packed module type. Splitting these two declarations is quite verbose, thus the shorthand notation combines both:
let rec f: type a. (module T with type t = a) -> 'never = fun m -> f m
I have a module type A with an exception. A will be implemented by B and C
module type A = sig
type t
val f: t->t->t
exception DivisionParZero
end
module B : A = struct
type t = int
let f a b=
if (a==0) then raise DivisionParZero else b/a
end
end
Ocamlc says when it compiles B:
Error: This variant expression is expected to have type exn
The constructor DivisionParZero does not belong to type exn
I don't understand why it doesn't work.
A is a signature that B needs to fulfill. In your context it means that you have to write the declaration line again:
module B : A = struct
type t = int
exception DivisionParZero
let f a b=
if (a==0) then raise DivisionParZero else b/a
end
You can experiment a little by returning a random value instead of raising the exception and you will see that the compiler tells you that your implementation does not fit the signature:
Error: Signature mismatch:
Modules do not match:
sig type t = int val f : int -> int -> int end
is not included in
A
The extension constructor `DivisionParZero' is required but not provided
File "test.ml", line 4, characters 2-27: Expected declaration
Excuse my potential misuse of terminology, I'm still not very comfortable with OCaml.
We have a functor with the following (abridged) signature:
module type FUNCTORA = sig
type input
type output
type key
type inter
val my_function : input list -> (key * output) list Deferred.t
end
Next, we implement it as such. MYAPP has the same types as above.
module MyFunctor (App : MyAPP) : FUNCTORA = struct
type input = App.input
type output = App.output
type key = App.key
type inter App.value
let my_function lst = ...
end
When trying to compile the implementation, we get this error:
Error: Signature mismatch:
...
Values do not match:
val my_function :
App.input list ->
(App.key * App.output) list Async_kernel.Deferred.t
is not included in
val my_function :
input list -> (key * output) list Async.Std.Deferred.t
It doesn't consider input to include App.input etc, even though we set them to be the same type. How can we get this to type check?
If I make the following changes:
MyAPP => FUNCTORA (* Since you say they are the same *)
type inter App.value => type inter = App.inter (* Syntax/name error *)
Deferred.t => option (* To limit dependence on other modules *)
Then your code compiles for me.
Possibly the problem is with Deferred.t. There are two distinct looking types in the error message.
In a module, I have defined a type that represents a graph node, which has a polymorphic data field i.e.
type 'a t = {data: 'a; adj: 'a t list}
How can I go about creating a Set of this data? I have tried the following (as per one of the suggestions here.
let cmp (g1:int Graph.t) (g2: int Graph.t) : int=
if phys_equal g1 g2 then
0
else
Int.compare g1.data g2.data
let make_set () =
let module Ord=struct
type t=int Graph.t
let compare=cmp
end
in (module Set.Make(Ord): Set.S with type elt=Ord.t)
But when I do, I get "The signature constrained by `with' has no component named elt"
I'm not sure exactly what you're trying to do, but if you just want to make sets of nodes whose data type is int, you don't need to use anything fancier than the usual OCaml module operations.
The following code works for me:
module Graph =
struct
type 'a t = {data: 'a; adj: 'a t list}
end
let cmp (g1:int Graph.t) (g2: int Graph.t) : int =
if g1 == g2 then
0
else
compare g1.Graph.data g2.Graph.data
module GSet = Set.Make(struct type t = int Graph.t let compare = cmp end)
Here's a session with the code:
$ ocaml
OCaml version 4.01.0
# #use "g.ml";;
module Graph : sig type 'a t = { data : 'a; adj : 'a t list; } end
val cmp : int Graph.t -> int Graph.t -> int = <fun>
module GSet :
sig
type elt = int Graph.t
. . .
end
# let myset = GSet.add { Graph.data = 14; adj = [] } GSet.empty;;
val myset : GSet.t = <abstr>
# GSet.is_empty myset;;
- : bool = false
I don't see a reason to constrain the module type, as Set.S is already the module type returned by Set.Make. But I am not a sophisticated user of OCaml module types.
That code works fine in the interpreter for me. Perhaps you've opened a different Set module without the elt type defined in S?
If I define the following Set in the interpreter:
# module Set = struct
module type S = sig end
end;;
module Set : sig module type S = sig end end
And then simply redefine make_set as you wrote it, I actually get the same error message. When trying out code with the interpreter, always keep in mind that you may be working with definitions you wrote previously.
As a rule of thumb, try to avoid binding values to names already in use in the libraries you wish to employ (I know it's tempting to shorten your names there, but at least add them a small distinctive prefix - for instance use ISet instead of Set in your case).
you can always run your code as a script, i.e. by launching it from the command line as follow:
$ ocaml my_script.ml
Or by the #use directive at the interpreter prompt. This let you write code snippets before testing them out with a fresh ocaml environment.
Finally, as in #Jeffrey's provided answer, an unpacked module is good enough for most purpose; your code was about instantiating a first class module, which is only interesting if you intend to pass that module around without the use of functors. See the documentation on modules (and related extensions of the language) for further explanation.
Working through Real World Haskell right now. Here's a solution to a very early exercise in the book:
-- | 4) Counts the number of characters in a file
numCharactersInFile :: FilePath -> IO Int
numCharactersInFile fileName = do
contents <- readFile fileName
return (length contents)
My question is: How would you test this function? Is there a way to make a "mock" input instead of actually needing to interact with the file system to test it out? Haskell places such an emphasis on pure functions that I have to imagine that this is easy to do.
You can make your code testable by using a type-class-constrained type variable instead of IO.
First, let's get the imports out of the way.
{-# LANGUAGE FlexibleInstances #-}
import qualified Prelude
import Prelude hiding(readFile)
import Control.Monad.State
The code we want to test:
class Monad m => FSMonad m where
readFile :: FilePath -> m String
-- | 4) Counts the number of characters in a file
numCharactersInFile :: FSMonad m => FilePath -> m Int
numCharactersInFile fileName = do
contents <- readFile fileName
return (length contents)
Later, we can run it:
instance FSMonad IO where
readFile = Prelude.readFile
And test it too:
data MockFS = SingleFile FilePath String
instance FSMonad (State MockFS) where
-- ^ Reader would be enough in this particular case though
readFile pathRequested = do
(SingleFile pathExisting contents) <- get
if pathExisting == pathRequested
then return contents
else fail "file not found"
testNumCharactersInFile :: Bool
testNumCharactersInFile =
evalState
(numCharactersInFile "test.txt")
(SingleFile "test.txt" "hello world")
== 11
This way your code under test needs very little modification.
As Alexander Poluektov already pointed out, the code you are trying to test can easily be separated into a pure and an impure part.
Nevertheless I think it is good to know how to test such impure functions in haskell.
The usual approach to testing in haskell is to use quickcheck and that's what I also tend to use for impure code.
Here is an example of how you might achieve what you are trying to do which gives you kind of a mock behavior * :
import Test.QuickCheck
import Test.QuickCheck.Monadic(monadicIO,run,assert)
import System.Directory(removeFile,getTemporaryDirectory)
import System.IO
import Control.Exception(finally,bracket)
numCharactersInFile :: FilePath -> IO Int
numCharactersInFile fileName = do
contents <- readFile fileName
return (length contents)
Now provide an alternative function (Testing against a model):
numAlternative :: FilePath -> IO Integer
numAlternative p = bracket (openFile p ReadMode) hClose hFileSize
Provide an Arbitrary instance for the test environment:
data TestFile = TestFile String deriving (Eq,Ord,Show)
instance Arbitrary TestFile where
arbitrary = do
n <- choose (0,2000)
testString <- vectorOf n $ elements ['a'..'z']
return $ TestFile testString
Property testing against the model (using quickcheck for monadic code):
prop_charsInFile (TestFile string) =
length string > 0 ==> monadicIO $ do
(res,alternative) <- run $ createTmpFile string $
\p h -> do
alternative <- numAlternative p
testRes <- numCharactersInFile p
return (testRes,alternative)
assert $ res == fromInteger alternative
And a little helper function:
createTmpFile :: String -> (FilePath -> Handle -> IO a) -> IO a
createTmpFile content func = do
tempdir <- catch getTemporaryDirectory (\_ -> return ".")
(tempfile, temph) <- openTempFile tempdir ""
hPutStr temph content
hFlush temph
hClose temph
finally (func tempfile temph)
(removeFile tempfile)
This will let quickCheck create some random files for you and test your implementation against a model function.
$ quickCheck prop_charsInFile
+++ OK, passed 100 tests.
Of course you could also test some other properties depending on your usecase.
* Note about the my usage of the term mock behavior:
The term mock in the object oriented sense is perhaps not the best here. But what is the intention behind a mock?
It let's you test code that needs access to a resource that usually is
either not available at testing time
or is not easily controllable and thus not easy to verify.
By shifting the responsibility of providing such a resource to quickcheck, it suddenly becomes feasible to provide an environment for the code under test that can be verified after a test run.
Martin Fowler describes this nicely in an article about mocks :
"Mocks are ... objects pre-programmed with expectations which form a specification of the calls they are expected to receive."
For the quickcheck setup I'd say that files generated as input are "pre-programmed" such that we know about their size (== expectation). And then they are verified against our specification (== property).
For that you will need to modify the function such that it becomes:
numCharactersInFile :: (FilePath -> IO String) -> FilePath -> IO Int
numCharactersInFile reader fileName = do
contents <- reader fileName
return (length contents)
Now you can pass any mock function that takes a file path and return IO string such as:
fakeFile :: FilePath -> IO String
fakeFile fileName = return "Fake content"
and pass this function to numCharactersInFile.
The function consists from two parts: impure (reading part content as String) and pure (calculating the length of String).
The impure part cannot be "unit"-tested by definition. The pure part is just call to the library function (and of course you can test it if you want :) ).
So there is nothing to mock and nothing to unit-test in this example.
Put it another way. Consider you have an equal C++ or Java implementation (*): reading content and then calculating its length. What would you really want to mock and what would remain for testing afterwards?
(*) which is of course not the way you will do in C++ or Java, but that's offtopic.
Based on my layman's understanding of Haskell, I've come to the following conclusions:
If a function makes use of the IO monad, mock testing is going to be impossible. Avoid hard-coding the IO monad in your function.
Make a helper version of your function that takes in other functions that may do IO. The result will look like this:
numCharactersInFile' :: Monad m => (FilePath -> m String) -> FilePath -> m Int
numCharactersInFile' f filePath = do
contents <- f filePath
return (length contents)
numCharactersInFile' is now testable with mocks!
mockFileSystem :: FilePath -> Identity String
mockFileSystem "fileName" = return "mock file contents"
Now you can verify that numCharactersInFile' returns the the expected results w/o IO:
18 == (runIdentity . numCharactersInFile' mockFileSystem $ "fileName")
Finally, export a version of your original function signature for use with IO
numCharactersInFile :: IO Int
numCharactersInFile = NumCharactersInFile' readFile
So, at the end of the day, numCharactersInFile' is testable with mocks. numCharactersInFile is just a variation of numCharactersInFile'.