I want to write a tail recursive function to print elements in a string list in separate lines like this:
# printlist ["a";"b";"c"];;
a
b
c
- : unit = ()
# printlist ["hello";"thanks"];;
hello
thanks
- : unit = ()
I was able to get it to work using print_endline with no problem:
let rec printlist strlist =
match strlist with
| [] -> print_endline ""
| hd::[] -> print_endline hd
| hd :: tl -> print_endline hd ; printlist tl;;
However, as soon as I switch to printf, it doesn't work anymore. What's wrong with my printf version?
let rec printlist strlist =
match strlist with
| [] -> printf ""
| hd::[] -> printf hd
| hd :: tl -> printf "%s\n" hd ; printlist tl;;
Error: This expression has type
(unit, out_channel, unit) format =
(unit, out_channel, unit, unit, unit, unit)
CamlinternalFormatBasics.format6
but an expression was expected of type string
In essence, you're trying to use printf without a format. The first argument of printf has to be a constant string. So you should have this:
printf "%s" hd
rather than this:
printf hd
To see why this is required, imagine what would happen if some of the strings in your input contained percent characters. Things would get out of control (type-wise) quite quickly.
In addition to Jeffrey's answer, I would suggest you use the standard library more in order to write more concise code.
List.iter, for example, calls a given function on all the elements of the list:
let print_list l = List.iter (fun e -> Printf.printf "%s\n" e) l
Using partial application smartly, you can make this line even shorter and more readable:
let print_list = List.iter (Printf.printf "%s\n")
The only difference with your function is the newline after the last element.
On the other hand, instead of printing elements one after another, a more functional and idiomatic approach would be to build the whole string first, and then print it.
Lucky you, the standard library got you covered. String.concat joins the elements in a string list together in one big string. You can also specify a string to use as a separator, and you don't have to worry about the newline after the last element.
let print_list l = print_string (String.concat "\n" l)
Related
There are many examples of how to read from and write to files, but many posts seem out of date, are too complicated, or are not 'safe' (1, 2) (they throw/raise exceptions). Coming from Rust, I'd like to explicitly handle all errors with something monadic like result.
Below is an attempt that is 'safe-er' because an open and read/write will not throw/raise. But not sure whether the close can fail. Is there a more concise and potentially safer way to do this?
(* opam install core batteries *)
open Stdio
open Batteries
open BatResult.Infix
let read_safe (file_path: string): (string, exn) BatPervasives.result =
(try let chan = In_channel.create file_path in Ok(chan)
with (e: exn) -> Error(e))
>>= fun chan ->
let res_strings =
try
let b = In_channel.input_lines chan in
Ok(b)
with (e: exn) -> Error(e) in
In_channel.close chan;
BatResult.map (fun strings -> String.concat "\n" strings) res_strings
let write_safe (file_path: string) (text: string) : (unit, exn) BatPervasives.result =
(try
(let chan = Out_channel.create file_path in Ok(chan))
with (e: exn) -> Error(e))
>>= fun chan ->
let res =
(try let b = Out_channel.output_string chan text in Ok(b)
with (e: exn) -> Error(e)) in
Out_channel.close chan;
res
let () =
let out =
read_safe "test-in.txt"
>>= fun str -> write_safe "test-out.txt" str in
BatResult.iter_error (fun e -> print_endline (Base.Exn.to_string e)) out
The Stdio library, which is a part of the Janestreet industrial-strength standard library, already provides such functions, which are, of course safe, e.g., In_channel.read_all reads the contents of the file to a string and corresponding Out_channel.write_all writes it to a file, so we can implement a cp utility as,
(* file cp.ml *)
(* file cp.ml *)
open Base
open Stdio
let () = match Sys.get_argv () with
| [|_cp; src; dst |] ->
Out_channel.write_all dst
~data:(In_channel.read_all src)
| _ -> invalid_arg "Usage: cp src dst"
To build and run the code, put it in the cp.ml file (ideally in a fresh new directory), and run
dune init exe cp --libs=base,stdio
this command will bootstrap your project using dune. Then you can run your program with
dune exec ./cp.exe cp.ml cp.copy.ml
Here is the link to the OCaml Documentation Hub that will make it easier for you to find interesting libraries in OCaml.
Also, if you want to turn a function that raises an exception to a function that returns an error instead, you can use Result.try_with, e.g.,
let safe_read file = Result.try_with ## fun () ->
In_channel.read_all file
You can read and write files in OCaml without needing alternative standard libraries. Everything you need is already built into Stdlib which ships with OCaml.
Here's an example of reading a file while ensuring the file descriptor gets closed safely in case of an exception: https://stackoverflow.com/a/67607879/20371 . From there you can write a similar function to write a file using the corresponding functions open_out, out_channel_length, and output.
These read and write file contents as OCaml's bytes type, i.e. mutable bytestrings. However, they may throw exceptions. This is fine. In OCaml exceptions are cheap and easy to handle. Nevertheless, sometimes people don't like them for whatever reason. So it's a bit of a convention nowadays to suffix functions which throw exceptions with _exn. So suppose you define the above-mentioned two functions as such:
val get_contents_exn : string -> bytes
val set_contents_exn : string -> bytes -> unit
Now it's easy for you (or anyone) to wrap them and return a result value, like Rust. But, since we have polymorphic variants in OCaml, we take advantage of that to compose together functions which can return result values, as described here: https://keleshev.com/composable-error-handling-in-ocaml
So you can wrap them like this:
let get_contents filename =
try Ok (get_contents_exn filename) with exn -> Error (`Exn exn)
let set_contents filename contents =
try Ok (set_contents_exn filename contents) with exn -> Error (`Exn exn)
Now these have the types:
val get_contents : string -> (bytes, [> `Exn of exn]) result
val set_contents : string -> bytes -> (unit, [> `Exn of exn]) result
And they can be composed together with each other and other functions which return result values with a polymorphic variant error channel.
One point I am trying to make here is to offer your users both, so they can choose whichever way–exceptions or results–makes sense for them.
Here's the full safe solution based on #ivg answer, using only the Base library.
open Base
open Base.Result
open Stdio
let read_safe (file_path: string) =
Result.try_with ## fun () ->
In_channel.read_all file_path
let write_safe (file_path: string) (text: string) =
Result.try_with ## fun () ->
Out_channel.write_all ~data:text file_path
let () =
let out =
read_safe "test-in.txt"
>>= fun str ->
write_safe "test-out.txt" str in
iter_error out ~f:(fun e -> print_endline (Base.Exn.to_string e))
I couldn't find an answer to my question among several ambiguous type variable error questions.
I'm currently trying to get this code I found to work. (https://gist.github.com/kirelagin/3886243)
My code:
import Control.Arrow
import Data.List
import qualified Data.Map as M
import Data.Function
main = do
putStrLn "Start test"
let foo = "Hello World"
let freqTest = freqList foo
putStrLn "Frequentie list"
print freqTest
putStrLn "Done.."
let treeTest = buildTree freqTest
putStrLn "Huffman Tree"
print treeTest
putStrLn "Done.."
let codeMaphTest = buildCodemap treeTest
putStrLn "Codemap ding"
-- print codeMaphTest
putStrLn "Done.."
--This typeclass is supposed to make life _a bit_ easier.
class Eq a => Bits a where
zer :: a
one :: a
instance Bits Int where
zer = 0
one = 1
instance Bits Bool where
zer = False
one = True
-- Codemap is generated from a Huffman tree. It is used for fast encoding.
type Codemap a = M.Map Char [a]
-- Huffman tree is a simple binary tree. Each leaf contains a Char and its weight.
-- Fork (node with children) also has weight = sum of weights of its children.
data HTree = Leaf Char Int
| Fork HTree HTree Int
deriving (Show)
weight :: HTree -> Int
weight (Leaf _ w) = w
weight (Fork _ _ w) = w
-- The only useful operation on Huffman trees is merging, that is we take
-- two trees and make them children of a new Fork-node.
merge t1 t2 = Fork t1 t2 (weight t1 + weight t2)
-- `freqList` is an utility function. It takes a string and produces a list
-- of pairs (character, number of occurences of this character in the string).
freqList :: String -> [(Char, Int)]
freqList = M.toList . M.fromListWith (+) . map (flip (,) 1)
-- `buildTree` builds a Huffman tree from a list of character frequencies
-- (obtained, for example, from `freqList` or elsewhere).
-- It sorts the list in ascending order by frequency, turns each (char, freq) pair
-- into a one-leaf tree and keeps merging two trees with the smallest frequencies
-- until only one tree is remaining.
buildTree :: [(Char, Int)] -> HTree
buildTree = bld . map (uncurry Leaf) . sortBy (compare `on` snd)
where bld (t:[]) = t
bld (a:b:cs) = bld $ insertBy (compare `on` weight) (merge a b) cs
-- The next function traverses a Huffman tree to obtain a list of codes for
-- all characters and converts this list into a `Map`.
buildCodemap :: Bits a => HTree -> Codemap a
buildCodemap = M.fromList . buildCodelist
where buildCodelist (Leaf c w) = [(c, [])]
buildCodelist (Fork l r w) = map (addBit zer) (buildCodelist l) ++ map (addBit one) (buildCodelist r)
where addBit b = second (b :)
-- Simple functions to get a Huffman tree or a `Codemap` from a `String`.
stringTree :: String -> HTree
stringTree = buildTree . freqList
stringCodemap :: Bits a => String -> Codemap a
stringCodemap = buildCodemap . stringTree
-- Time to do the real encoding and decoding!
-- Encoding function just represents each character of a string by corresponding
-- sequence of `Bit`s.
encode :: Bits a => Codemap a -> String -> [a]
encode m = concat . map (m M.!)
encode' :: Bits a => HTree -> String -> [a]
encode' t = encode $ buildCodemap t
-- Decoding is a little trickier. We have to traverse the tree until
-- we reach a leaf which means we've just finished reading a sequence
-- of `Bit`s corresponding to a single character.
-- We keep doing this to process the whole list of `Bit`s.
decode :: Bits a => HTree -> [a] -> String
decode tree = dcd tree
where dcd (Leaf c _) [] = [c]
dcd (Leaf c _) bs = c : dcd tree bs
dcd (Fork l r _) (b:bs) = dcd (if b == zer then l else r) bs
Output:
huffmancompress.hs:17:24: error:
* Ambiguous type variable `a0' arising from a use of `buildCodemap'
prevents the constraint `(Bits a0)' from being solved.
Relevant bindings include
codeMaphTest :: Codemap a0 (bound at huffmancompress.hs:17:9)
Probable fix: use a type annotation to specify what `a0' should be.
These potential instances exist:
instance Bits Bool -- Defined at huffmancompress.hs:35:10
instance Bits Int -- Defined at huffmancompress.hs:31:10
* In the expression: buildCodemap treeTest
In an equation for `codeMaphTest':
codeMaphTest = buildCodemap treeTest
In the expression:
do putStrLn "Start test"
let foo = "Hello World"
let freqTest = freqList foo
putStrLn "Frequentie list"
....
|
17 | let codeMaphTest = buildCodemap treeTest
| ^^^^^^^^^^^^^^^^^^^^^
I've tried serveral things I found on the internet but nothing worth mentioning to be honest.
Maybe any of you guys can help me out!
On line 17, where the error points you:
let codeMaphTest = buildCodemap treeTest
What type is codeMaphTest? Should it be Codemap Int? Or Codemap String? Or, perhaps, Codemap Bool? The function buildCodemap can return any type, as long as it has an instance of Bit. So what type should it be?
The compiler doesn't know. There is nowhere to glean this information from. It's ambiguous.
And this is exactly what the compiler is telling you: "ambiguous type variable".
One way to fix this is to provide a type annotation (exactly as the error message says, by the way):
let codeMaphTest :: Codemap Int = buildCodemap treeTest
Note that I chose Int just as an example, because I don't know which type you meant (I'm somewhat like the compiler in that respect). Please substitute your own type - the one you actually wanted there.
Your code is indeed ambiguous. buildCodemap treeTest has a polymorphic type Bits a => Codemap a, so it can be used as a Codemap Int, a Codemap Bool, or even as another type if you defines further instances of Bits.
This is not a problem, on its own, but later on you try to use this value (e.g., to print it), so we really need to pick a concrete type a.
You could pick a at the definition point:
let codeMaphTest :: Codemap Int
codeMaphTest = buildCodemap treeTest
Or, alternatively, you could choose a later on, where you use it
print (codeMaphTest :: Codemap Int)
I am trying to make a module that would allow to create a table in ocaml. It would do a query called project to limit the table's values. However on the last line of the definition of the function chooser I am getting syntax error.
module type TABLE =
sig
type database
type table
val create_table: string list * string list* (string list) list -> table
val printTable : table -> string
val listToString : string list -> string
val project : string list * table -> table
val chooser : string list * string list-> string list
end;;
module UsingTable : TABLE =
struct
type table = (string list * string list* (string list) list)
type database = table list
let create_table (a,b,c) = (a,b,c)
let chooser inputList = (
for i = 0 to (List.length trueFalseList-1) do
if List.nth trueFalseList i = "True"
then
(List.nth inputList i)::ans
done
List.rev ans;;)
let project (conditions, aTable)= (
let rec innerProc tmp= function
n,[],v->List.rev tmp
|n,cH::cT,v-> if List.mem cH conditions
then innerProc (["True"]::tmp) (n,cT,v)
else innerProc (["False"]::tmp) (n,cT,v)
in
let trueFalseList = innerProc [] aTable
let rec finalListCreator = match aTable with
n,[],[]->n,[],[]
|n,cH::cT,[]->n,chooser cH ::finalListCreator cT,[]
|n,c,h::t -> n,c,chooser h ::finalListCreator t
)
let rec listToString aList = match aList with
[] -> ""
| h::t -> "\t"^h^"\t"^listToString t
let rec printTable aTable = match aTable with
[],[],[] -> ""
| [],[],vH::vT -> "\n"^(listToString vH)^printTable ([],[],vT)
| [],cH::cT,v -> "\t"^cH^"\t"^printTable([],cT, v)
| n, c , v-> "\n"^(List.hd n)^"\n\n"^printTable([],c, v)
end;;
let atable =UsingTable.create_table (["Student"], ["Id";"Name";"Gender";"Course"],
[["001";"Jim";"M";"AlgoDS"];
["002";"Linnea";"F";"Databases"];
["003";"Anna";"F";"C#"];
["004";"Abby";"F";"C#"];
["005";"Arthur";"M";"JavaScript"]]);;
print_string (UsingTable.printTable atable) ;;
These lines have at least two syntax problems:
let chooser inputList = (
for i = 0 to (List.length trueFalseList-1) do
if List.nth trueFalseList i = "True"
then
(List.nth inputList i)::ans
done
List.rev ans;;)
First, the for .. done is one expression, and List.rev ans is another expression. You need a semicolon (;) between them.
Second, you should use ;; only when you want the input up to that point to be processed. But here if you process the input at the ;; you are missing a right parenthesis.
In my opinion, you should be entering ;; only at the toplevel. The best way to think of this token is as an instruction to the toplevel. It's not part of normal OCaml syntax.
These are only the first two errors. There are quite a few other errors in the code. It might be good to add one function at a time to the module so you can concentrate on a few problems at a time.
Update
The environment you're using is a little bit extra complicated because it has an Evaluate button that asks to evaluate what you've typed so far. This makes the ;; token much less useful.
It would be a good discipline to use this environment without using the ;; token at all. Just click the Evaluate button when you want an evaluation.
The main trick is if you want to evaluate a statement (a unit-valued expression in OCaml) at the outer level, like say Printf.printf "hello world\n". The usual idiom to avoid putting ;; before this is to make it into a declaration like so:
let () = Printf.printf "hello world\n"
That is the one non-obvious idiom that people use when writing source code (where the ;; almost never appears in my experience).
I've written this mergesort implementation, which works fine if I put the divide function outside of the mergesort function. But when I try to make divide an inner function of mergesort I encounter a syntax error.
I know, there must be some really simple explanation for this. I've looked all over the internet, yet found nothing.
Here is the code:
let mergesort list =
let rec sort lists acc = (
let rec merge sublist1 sublist2 merged_list =
match sublist1 with
|[] -> merged_list # sublist2
|hd1 :: tl1 ->
match sublist2 with
|[] -> merged_list # sublist1
|hd2 :: tl2 ->
if hd1 < hd2 then merge tl1 sublist2 (merged_list # hd1::[])
else merge sublist1 tl2 (merged_list # hd2::[])
in match lists with
|[] ->
(match acc with
|[] -> []
|hd :: [] -> hd
|_ -> sort acc [])
|hd :: tl -> sort (List.tl tl) ((merge (List.hd tl) hd [])::acc)
)
and rec divide list list_of_lists = (
match list with
[] -> list_of_lists
|hd :: tl -> divide tl ((hd :: []) :: list_of_lists)
)
in sort (divide list []) []
;;
and it results into:
Characters 567-570:
and rec divide list list_of_lists = (
^^^
Error: Syntax error
A local definition has the following syntax in OCaml:
let [rec] pattern1 = expr1 and … and patternN = exprN in expr
Thus an extra rec is not allowed after the and keyword, and is allowed only after the first let. The rec flag extends to all values defined in the local definition, thus you just need to remove this erroneous rec after and.
You need to simply remove the rec keyword from your definition there.
This is because when you use the and keyword, you’re effectively repeating the previous definition syntactically, which in this case is let rec.
So your current implementation is effectively the same as saying let rec rec
I'm a OCaml newbie. I'm playing around with "hello world" type snippets and came across this situation.
Here's a session with the interpreter with some extra comments:
# let average a b =
(a +. b) /. 2.;;
val average : float -> float -> float = <fun>
# average 1. 4.;;
- : float = 2.5
# string_of_float (average 1. 4.);;
- : string = "2.5"
(* this fails...*)
# let _ = Printf.printf (string_of_float (average 1. 4.));;
Error: This expression has type string but an expression was expected of type
('a, out_channel, unit) format =
('a, out_channel, unit, unit, unit, unit) format6
(* yet this works *)
# "hello!";;
- : string = "hello!"
# let _ = Printf.printf "hello!";;
hello!- : unit = ()
(* another failed attempt *)
# let s = string_of_float (average 1. 4.);;
val s : string = "2.5"
# s;;
- : string = "2.5"
# let _ = Printf.printf s;;
Error: This expression has type string but an expression was expected of type
('a, out_channel, unit) format =
('a, out_channel, unit, unit, unit, unit) format6
(* and this also works?? *)
# let _ = Printf.printf "2.5";;
2.5- : unit = ()
So here's the situation. string_of_float (average 1. 4.) returns a string,
just as "hello!" does. When I give "hello!" into Printf.printf, it works
as expected. When I give string_of_float (average 1. 4.) to Printf.printf it fails and tells
me it expected didn't expect a string but that other weird type. But why do "hello!" and "2.5" work then?
What's going on?
There is a kind of "overloading" of the meaning of string literals in OCaml. At compile time, they can either be interpreted as a string, or as a format (which are completely different things in the type system), depending on what the type-checker thinks. If it decides that it should be a format, then the format string is parsed directly at compile-time (that is why it is able to type-check the arguments to printf at compile time). (Unlike C, which parses the string at runtime.) However, there is no simple way to convert from a string into a format at runtime. So when you see Printf.printf "2.5", the "2.5" is actually not a string, but as a special format type that was parsed at compile-time. That is why you cannot substitute a string for it.
On an unrelated note, if you just want to print a string, you might want to use print_string (or print_endline if you want a newline).
Printf.printf "%s" anyStringExpr
will work. The first argument to printf is somewhat magical. (Others will fill in the details.)