Scripted main in OCaml? - ocaml

How can I emulate this Python idiom in OCaml?
if __name__=="__main__":
main()
See RosettaCode for examples in other programming languages.

There is no notion of main module in Ocaml. All the modules in a program are equal. So you can't directly translate this Python idiom.
The usual way in Ocaml is to have a separate file containing the call to main, as well as other stuff like command line parsing that only make sense in a standalone executable. Don't include that source file when linking your code as a library.
There is a way to get at the name of the module, but it's rather hackish, as it is intended for debugging only. It violates the usual assumption that you can rename a module without changing its behavior. If you rely on it, other programmers reading your code will curse you. This method is provided for entertainment purposes only and should not be used in real life.
let name_of_this_compilation_unit =
try assert false with Assert_failure (filename, _, _) -> filename
You can compare the name of the compilation unit with Sys.executable_name or Sys.argv.(0). Note that this is not really the same thing as the Python idiom, which does not rely on the toplevel script having a particular name.

$ ocamlc -o scriptedmain -linkall str.cma scriptedmain.ml
$ ./scriptedmain
Main: The meaning of life is 42
$ ocamlc -o test -linkall str.cma scriptedmain.ml test.ml
$ ./test
Test: The meaning of life is 42
scriptedmain.ml:
let meaning_of_life : int = 42
let main () = print_endline ("Main: The meaning of life is " ^ string_of_int meaning_of_life)
let _ =
let program = Sys.argv.(0)
and re = Str.regexp "scriptedmain" in
try let _ = Str.search_forward re program 0 in
main ()
with Not_found -> ()
test.ml:
let main () = print_endline ("Test: The meaning of life is " ^ string_of_int Scriptedmain.meaning_of_life)
let _ =
let program = Sys.argv.(0)
and re = Str.regexp "test" in
try let _ = Str.search_forward re program 0 in
main ()
with Not_found -> ()
Posted on RosettaCode.

Related

[OCaml flow_parser) No implementations provided for the following modules

I am trying to use the flow_parser library, but I get the following error after running dune exec ./start.exe:
File "_none_", line 1:
Error: No implementations provided for the following modules:
Ppx_deriving_runtime referenced from /Users/macintosh/.opam/4.07.1/lib/flow_parser/parser_flow.cmxa(File_key)
Here's my dune file:
(executables
(names start)
(libraries core flow_parser)
(preprocess (pps ppx_jane ppx_deriving.show ppx_deriving.ord)))
Here's the start.ml:
let () =
let source = File_key.SourceFile("./fixtures/fib.js") in
let channel = open_in "./fixtures/fib.js" in
let lexbuf = Sedlexing.Utf8.from_channel channel in
let lex_env = Lex_env.new_lex_env (Some source) lexbuf ~enable_types_in_comments: false in
let (_, lex_result) = Lexer.token lex_env in
print_endline(Lex_result.debug_string_of_lex_result(lex_result));
Note that I could not use open Flow_parser - I get an unbounded module error. However, my editor (VSCode with the OCaml/ReasonML plugin) allows me to access the modules inside Flow_parser (e.g. File_key).

How to read a file, and write it to a different file

I hope all is well. At this point, I plan on reading a file, and then writing it to another file. (The equivalent of cp in OCaml, before I go ahead and manipulate text.) Currently in my code, I am making using the extlib to read a file, and then output it. I am also using dune to build file as an executable file. My file looks something like the following:
(* example.ml file *)
let read_filename = "example_1.ts"
let filename = "example_2.ts"
let () =
let text read_filename =
let chan = open_in read_filename in
Std.input_list chan
let filename = filename in
let text = text in
Std.output_file ~filename ~text
(* normal exit: all channels are flushed and closed *)
When I build the file using dune build example.bc I receive the following error:
File "example.ml", line 11, characters 2-5:
Error: Syntax error
I am trying to figure out what I am doing wrong, but to no avail. Any help would be more than appreciated. Thank you.
When I have readed you code I remark these lines.
let filename = filename in
let text = text in
Why have you writting this line ? (* You could delete it safely *)
And then I have found your syntax error was a line above you have forgotten a in
(* example.ml file *)
let read_filename = "example_1.ts"
let filename = "example_2.ts"
let () =
let text read_filename =
let chan = open_in read_filename in
Std.input_list chan
in
Std.output_file ~filename ~text
(* normal exit: all channels are flushed and closed *)
The rule for each local definition you must have a in keyword
(* a local definition is a let inside a let expression *)

Type error when passing Ocaml a file from the command line but not in repl

My code gives an error when run from a file but runs fine when pasted into the Ocaml repl. I have saved the following as test.ml:
module StringSet = Set.Make(String)
let words = StringSet.add StringSet.empty "something";;
When run from bash with "ocaml test.ml" I get:
File "test.ml", line 3, characters 26-41:
Error: This expression has type StringSet.t = Set.Make(String).t
but an expression was expected of type StringSet.elt = string
When pasted into the Ocaml repl I get:
# module StringSet = Set.Make(String)
let words = StringSet.add StringSet.empty "something";;
module StringSet :
sig
(* ... much more output ... *)
end
val words : StringSet.t = <abstr>
#
Everything seems to work fine from the repl.
My Ocaml version is reported by the repl as: OCaml version 4.02.1.
Does anyone know why the error is produced when running "ocaml test.ml"?
The StringSet.add function takes the element (the string) as the first parameter and the set (StringSet.empty) as the second parameter. You have them in the opposite order.
When I try your code I get the same error in both cases. When I invert the parameter order I don't get an error in either case.
I'm using OCaml 4.06.0, but I would really doubt that the paramter order has changed.

Referring to module types defined in toplevel files

In OCaml, if your project has a file called code.ml you can refer to it in other files using the module name Code. I was wondering if you defined an .mli file if you could refer to the signature it defines in a similar way. For example if you had a file called wow.mli and you could have another file with the declaration
module Func(St : Wow) = struct ... end
Is there a way to do something along those lines?
This works for me:
module Func(St: module type of Wow) = struct ... end
In detail here's what I did:
$ cat wow.mli
val f : int -> int
$ cat m.ml
module Func (St: module type of Wow) = struct let f x = St.f x end
$ ocamlopt -c wow.mli
$ ocamlopt -c m.ml

Building a haskell interpreter (hint) as dynamic library, useable from C++: Missing Interpreter.dyn_hi

I want to create a haskell interpreter that I can use from C++ on linux.
I have a file FFIInterpreter.hs which implements the interpreter in haskell and exports the functions via FFI to C++.
module FFIInterpreter where
import Language.Haskell.Interpreter
import Data.IORef
import Foreign.StablePtr
import Foreign.C.Types
import Foreign.C.String
import Control.Monad
import Foreign.Marshal.Alloc
type Session = Interpreter ()
type Context = StablePtr (IORef Session)
foreign export ccall createContext :: CString -> IO Context
createContext :: CString -> IO Context
createContext name = join ((liftM doCreateContext) (peekCString name))
where
doCreateContext :: ModuleName -> IO Context
doCreateContext name
= do let session = newModule name
_ <- runInterpreter session
liftIO $ newStablePtr =<< newIORef session
newModule :: ModuleName -> Session
newModule name = loadModules [name] >> setTopLevelModules [name]
foreign export ccall freeContext :: Context -> IO ()
freeContext :: Context -> IO ()
freeContext = freeStablePtr
foreign export ccall runExpr :: Context -> CString -> IO CString
runExpr :: Context -> CString -> IO CString
runExpr env input = join ((liftM newCString) (join (((liftM liftM) doRunExpr) env (peekCString input))))
where
doRunExpr :: Context -> String -> IO String
doRunExpr env input
= do env_value <- deRefStablePtr env
tcs_value <- readIORef env_value
result <- runInterpreter (tcs_value >> eval input)
return $ either show id result
foreign export ccall freeString :: CString -> IO ()
freeString :: CString -> IO ()
freeString = Foreign.Marshal.Alloc.free
When I compile the whole project with ghc, everything works fine. I use the following command:
ghc -no-hs-main FFIInterpreter.hs main.cpp -lstdc++
But the haskell module is only a small piece of the C++ project and I don't want the whole project to depend on ghc.
So I want to build a dynamic library with ghc and then link it to the project using g++.
$ ghc -shared -fPIC FFIInterpreter.hs module_init.c -lstdc++
[1 of 1] Compiling FFIInterpreter ( FFIInterpreter.hs, FFIInterpreter.o )
Linking a.out ...
/usr/bin/ld: /usr/lib/haskell-packages/ghc/lib/hint-0.3.3.2/ghc-7.0.3/libHShint-0.3.3.2.a(Interpreter.o): relocation R_X86_64_32S against `.data' can not be used when making a shared object; recompile with -fPIC
/usr/lib/haskell-packages/ghc/lib/hint-0.3.3.2/ghc-7.0.3/libHShint-0.3.3.2.a: could not read symbols: Bad value
collect2: ld gab 1 als Ende-Status zurück
So I added the -dynamic keyword, but that also doesn't work:
$ ghc -dynamic -shared -fPIC FFIInterpreter.hs librarymain.cpp -lstdc++
FFIInterpreter.hs:3:8:
Could not find module `Language.Haskell.Interpreter':
Perhaps you haven't installed the "dyn" libraries for package `hint-0.3.3.2'?
Use -v to see a list of the files searched for.
I searched my system for Interpreter.dyn_hi but didn't find it. Is there a way to get it?
I also tried to install hint manually, but this also doesn't deliver the Interpreter.dyn_hi file.
You have to install the library (and all it depends on) with the --enable-shared flag (using cabal-install) to get the .dyn_hi and .dyn_o files. You may consider setting that option in your ~/.cabal/config file.
Perhaps the easiest way is to uncomment the shared: XXX line in ~/.cabal/config, set the option to True and
cabal install --reinstall world
For safety, run that with the --dry-run option first to detect problems early. If the --dry-run output looks reasonable, go ahead and reinstall - it will take a while, though.