3

I was creating a simple concat, and thought that this might work.

(** [foldl fun init lst] the [fun] gets applied, left to right: 
 {[foldl fun init [e1;e2;e3;e4] -> fun e4 (fun e3 (fun e2 (fun e1 init)))]} *)
let foldl func = let rec foldl accum = function  
                   |[] -> accum
                   |(head :: tail) -> foldl (func head accum) tail
  in foldl 

(** [reverse lst] reverses [lst]. Useful for tail recursive list building functions *)      
let reverse : 'a list -> 'a list = foldl (fun a b -> a :: b) []
let concat lst = lst |> reverse |> foldl (foldl (fun a b -> a :: b)) [] |> reverse

This only works when I try this

(** [foldl fun init lst] the [fun] gets applied, left to right: 
 {[foldl fun init [e1;e2;e3;e4] -> fun e4 (fun e3 (fun e2 (fun e1 init)))]} *)
let foldl func = let rec foldl accum = function  
                   |[] -> accum
                   |(head :: tail) -> foldl (func head accum) tail
  in foldl 

(** [reverse lst] reverses [lst]. Useful for tail recursive list building functions *)      
let reverse : 'a list -> 'a list = foldl (fun a b -> a :: b) []
let reverse' : 'a list -> 'a list = foldl (fun a b -> a :: b) []
let concat lst = lst |> reverse' |> foldl (foldl (fun a b -> a :: b)) [] |> reverse

I heard this was related to the value restriction, but I don't know how to make it work. What should I do?

2 Answers 2

3

You can't use partial application in a place like this without running head-first into the value restriction. As noted, you can eta-expand to avoid this.

If you resolve this there's no need for both reverse and reverse'.

I also wanted to note that your foldl is a bit cumbersome. There's no need to define a local function vs. just passing multiple arguments. Is this "lying to ourselves" about how OCaml conceptually works ? Maybe. It's also cleaner and easier to read.

let rec foldl func accum = function
  | [] -> accum
  | head::tail -> foldl func (func head accum) tail
Sign up to request clarification or add additional context in comments.

2 Comments

I didn't want to repeatedly pass func into foldl, I was taking inspiration from haskell, which has a lot of functions to find like mine in Data.List. Are there any other things that I need to know?
Is there any performance penalty? Or does tail-call optimization solve it?
3

As mentioned in the manual, one way around this is to η-expand the function:

# let reverse : 'a list -> 'a list = fun x -> foldl (fun a b -> a :: b) [] x;;
val reverse : 'a list -> 'a list = <fun>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.