I'm currently trying to learn f# using the book Real-World Functional Programming by Petricek and Skeet (2010) but have been encountering problems when using continuations to avoid stack overflow.
The problem that I have been encountering is that my code using continuations works perfectly when launched in the f# interactive, but still causes stack overflow when placing the code in the program.fs file and then launching it through the debugger in Visual Studio.
It is unclear to me why this happens, and would very much appreciate if anyone could give me an explanation to why this happens.
In case the version of Visual Studio is relevant, I am using:
Visual Studio Ultimate 2012
Version 11.0.61030.00 Update 4
The .Net framework used is:
Version. 4.5.51641
The code presented in the book that is causing this problem is presented below:
open System
let rand = new Random()
//A tree is either a leaf with a value or a node that contains two children
type IntTree =
| Leaf of int
| Node of IntTree * IntTree
//A list of that will decide all leaf values
let numbers2 = List.init 1000000 (fun _ -> rand.Next(-50,51))
///Creates an imbalanced tree with 1000001 leafs.
let imbalancedTree2 =
numbers2 |> List.fold (fun currentTree num ->
Node(Leaf(num), currentTree)) (Leaf(0))
//Sums all leafs in a tree by continuation and will execute the inserted function with the total
//sum as argument once all values have been summed.
let rec sumTreeCont tree cont =
match tree with
| Leaf(num) -> cont(num)
| Node(left, right) ->
sumTreeCont left (fun leftSum ->
sumTreeCont right (fun rightSum ->
cont(leftSum + rightSum)))
//Sums the imbalanced tree using the sumTreeCont function
let sumOfTree = sumTreeCont imbalancedTree2 (fun x -> x)
Thanks in advance!
//Tigerstrom
If you are running the program in Debug mode, then the default Visual Studio project setting disables tail calls. The main reason is that, with tail calls enabled, you do not get very useful information in the call stack (which makes debugging harder).
To fix this, you can go to your project options and check "Generate tail calls" on the "Build" page. In release mode, this is enabled by default.
Related
i have a solution called Algos
on Solution explorer i have 2 projects inside this solution
one called Algos (again ! maybe i should change the name for avoiding confusion ?)
which is a console application
one called MyLibrary which is a Class Library
I have in the solution explorer added in the References of the Project Algo MyLibrary and i can see it in the list.
// useful functions
// returns the minimum + index of the minimum
namespace Misc
exception InnerError of string
module Search =
let mini (s : (int*int) list) =
match s with
| [] -> (-1,(-1,-1))
| _ -> s |> Seq.mapi (fun i x -> (i, x)) |> Seq.minBy snd
let maxi (s : (int*int) list) =
match s with
| [] -> (-1,(-1,-1))
| _ -> s |> Seq.mapi (fun i x -> (i, x)) |> Seq.maxBy snd
module Bit =
let rec sumbits (n:int):int=
let rec helper acc m =
match m with
| 0 -> acc
| 1 -> acc+1 // enlever cela ?
| _ -> let r = m%2
helper (acc+r) (m>>>1)
helper 0 n
let power2 k =
let powers_of_2 = [|1;2;4;8;16;32;64;128;256;512;1024;2048;4096;8192;16384;32768;65536;131072;262144;524288;1048576;2097152;4194304;8388608;16777216|]
if ((k >= 24) || (k<0)) then raise (InnerError("power exponent not allowed"))
else powers_of_2.[k]
i'm just to use Misc.Bit.power2 in the main code
open MyLibrary
let a = Misc.Bit.power2 3
but Misc.Bit will be underlined and I have a compiler error
Severity Code Description Project File Line Suppression State
Error The value, constructor, namespace or type 'Bit' is not defined Algos C:\Users\Fagui\Documents\GitHub\Learning Fsharp\Algos\Algos\TSP.fs 50
what have i done wrong ? does it come from other parts of the source code perhaps ?
there are no other warnings.
both projects use .NET Framework 4.5.2
MyLibrary uses Target F# Runtime 4.3.1 while there is no similar indication for Algos.
thanks
Two things come to mind here: I don't see where the namespace MyLibrary is defined, and you haven't mentioned that you actually compiled the dependency.
Generally, to reference and use a library within the same solution, you need to:
add the library to the using program's dependency, preferably via: References – right-click – add Reference (Reference Manager) – Projects – Solution, by checking the checkbox of the dependency.
compile the dependency. When referenced properly, this should automatically occur before dependent code is compiled, but IntelliSense will only update after compiles! So hit compile when you see outdated errors.
use identifiers from the library via their correct namespace. Make sure that the qualified names or open declarations in the using code are correct.
To my knowledge, this should be all you need to do.
There are rare cases where files get write-locked but never released on compilation and you need to restart Visual Studio to be able to compile again. I've also encountered a case where some interaction of Git and Visual Studio created corrupted, half-deleted files with effectively no file owner; this required a reboot to fix. If you're really scratching your head and the errors are clearly nonsensical, maybe try moving the folder to check for file system damage.
This should certainly work. Please also make sure that you target same versions of F# and .NET: .NET 4.5.2 and 4.4.0.0 in the library and the console application
Tested on F# 3.1 on windows 7
fsi.PrintLength <- 5000;;
[1..5000];;
Process is terminated due to StackOverflowException.
Session termination detected. Press Enter to restart.
on Mono (F# 4.0), there doesn't seem to be such a limitation.
I think this is a bug in the formatting module that takes care of pretty printing to F# Interactive.
There are some non-tail recursive functions that uses PrintLength e.g. boundedUnfoldL in this line. Implementation of boundedUnfoldL is indeed not tail-recursive:
let boundedUnfoldL
(itemL : 'a -> layout)
(project : 'z -> ('a * 'z) option)
(stopShort : 'z -> bool)
(z : 'z)
maxLength =
let rec consume n z =
if stopShort z then [wordL "..."] else
match project z with
| None -> [] // exhaused input
| Some (x,z) -> if n<=0 then [wordL "..."] // hit print_length limit
else itemL x :: consume (n-1) z // cons recursive...
consume maxLength z
I don't know why it doesn't blow up on Mono. It would be surprising if F# Interactive on Mono can handle length > 5000 successfully.
You can report this as a bug to https://visualfsharp.codeplex.com/workitem/list/basic.
I am new(ish) to F# and am trying to get a tree representation of a filesystem directory. Here's what I came up with:
type FSEntry =
| File of name:string
| Directory of name:string * entries:seq<FSEntry>
let BuildFSDirectoryTreeNonTailRecursive path =
let rec GetEntries (directoryInfo:System.IO.DirectoryInfo) =
directoryInfo.EnumerateFileSystemInfos("*", System.IO.SearchOption.TopDirectoryOnly)
|> Seq.map (fun info ->
match info with
| :? System.IO.FileInfo as file -> File (file.Name)
| :? System.IO.DirectoryInfo as dir -> Directory (dir.Name, GetEntries dir)
| _ -> failwith "Illegal FileSystemInfo type"
)
let directoryInfo = System.IO.DirectoryInfo path
Directory (path, GetEntries directoryInfo)
But... pretty sure that isn't tail recursive. I took a look at the generated IL and didn't see any tail prefix. Is there a better way to do this? I tried using an accumulator but didn't see how that helps. I tried mutual recursive functions and got nowhere. Maybe a continuation would work but I found that confusing.
(I know that stack-depth won't be an issue in this particular case but still would like to know how to tackle this non-tail recursion problem in general)
OTOH, it does seem to work. The following prints out what I am expecting:
let PrintFSEntry fsEntry =
let rec printFSEntryHelper indent entry =
match entry with
| File name -> printfn "%s%s" indent name
| Directory(name, entries) ->
printfn "%s\\%s" indent name
entries
|> Seq.sortBy (function | File name -> 0 | Directory (name, entries) -> 1)
|> Seq.iter (printFSEntryHelper (indent + " "))
printFSEntryHelper "" fsEntry
This should probably be a different question but... how does one go about testing BuildFSDirectoryTreeNonTailRecursive? I suppose I could create an interface and mock it like I would in C#, but I thought F# had better approaches.
Edited: Based on the initial comments, I specified that I know stack space probably isn't an issue. I also specify I'm mainly concerned with testing the first function.
To expand on my comment from earlier - unless you anticipate working with inputs that would cause a stack overflow without tail recursion, there's nothing to be gained from making a function tail-recursive. For your case, the limiting factor is the ~260 characters in path name, beyond which most Windows APIs will start to break. You'll hit that way before you start running out of stack space due to non-tail recursion.
As for testing, you want your functions to be as close to a pure function as possible. This involves refactoring out the pieces of the function that are side-effecting. This is the case with both of your functions - one of them implicitly depends on the filesystem, the other prints text directly to the standard output.
I guess the refactoring I suggest is fairly close to Mark Seemann's points: few mocks - checked, few interfaces - checked, function composition - checked. The example you have however doesn't lend itself nicely to it, because it's an extremely thin veneer over EnumerateFileSystemInfo. I can get rid of System.IO like this:
type FSInfo = DirInfo of string * string | FileInfo of string
let build enumerate path =
let rec entries path =
enumerate path
|> Seq.map (fun info ->
match info with
| DirInfo (name, path) -> Directory(name, entries path)
| FileInfo name -> File name)
Directory(path, entries path)
And now I'm left with an enumerate: string -> seq<FSInfo> function that can easily be replaced with a test implementation that doesn't even touch the drive. Then the default implementation of enumerate would be:
let enumerateFileSystem path =
let directoryInfo = DirectoryInfo(path)
directoryInfo.EnumerateFileSystemInfos("*", System.IO.SearchOption.TopDirectoryOnly)
|> Seq.map (fun info ->
match info with
| :? System.IO.FileInfo as file -> FileInfo (file.Name)
| :? System.IO.DirectoryInfo as dir -> DirInfo (dir.Name, dir.FullName)
| _ -> failwith "Illegal FileSystemInfo type")
You can see that it has virtually the same shape as the build function, minus recursion, since the entire 'core' of your logic is in EnumerateFileSystemInfos which lives beyond your code. This is a slight improvement, not in any way test-induced damage, but still it's not something that will make it onto anyone's slides anytime soon.
I have an issues concerning the F# on mono. Im doing this course in functional programming at my university. In the course we are using F#, and I uses Xamarin as my editor.
The thing is that we had a lesson on tail recursion, as a tool for getting efficiency. But when you are not able to write your function tail recursive, we had to use continuous, such that we using the heap and not the stack.
This seems not to work on mono 3.10.0 with F# 3.1, I get an System.StackOverflowException. This should be impossible to get, due the continuous should use the heap.
let rec fibC n c =
match n with
|0 -> c 0
|1 -> c 1
|n -> fibC (n-1) (fun v1 -> fibC (n-2) (fun v2 -> c(v1+v2)))
I tested a Fibonacci implementation passing an accumulator instead of a function (continuation) like this:
let fib n =
let rec _fib i (a,b) =
match i with
| 0 -> a
| _ -> _fib (i-1) (b, a+b)
_fib n (0,1)
which worked fine on Mono, i.e. no stack overflow.
So I guess it's only an issue with TCO when using continuations. There's a Xamarin ticket from June 2013 addressing this.
I have some code in F# that works fine under .net but overflows the stack under Mono. A related issue is that it seems to do so long before the stack space supposedly available to it runs out (it is started with System.Threading.Thread (ts, 1000000000)). As far as I can tell, the fold it dies in is tail-recursive and the stack trace looks as if tail-optimization is not being done. I am running 3.2.1 with --optimize=tailc.
Does somebody please know exactly what kinds of tail calls remove the calling stack and which do not? Or alternatively how to allocate more stack? Many thanks.
I am aware of Tailcall elimination in Mono
EDIT: here is an outline of the code as requested in the comments. It is a part of a fold over a large data structure, but the failing stacktrace has just mapk and myfold on it.
let rec myfold f x k =
let rec mapk xs k =
match xs with
[] -> k []
| x::xs -> mapk xs (fun xs' -> myfold f x (fun x' -> (x' :: xs') |> k))
...
mapk (...) ( ... >> k)
As far as I know, --optimize=tailc isn't a supported F# compiler flag.
I don't think there's a way to enable/disable tailcall-optimization support in Mono (from the command-line, anyway); the F# compiler flag to enable tail-call optimizations is --tailcalls+, but according to Compiler Options (F#) that's on by default.
I think your best choices to get this resolved are:
File a bug report with Xamarin
Go on the #monodev IRC channel (on irc.gnome.org) and see if one of the developers/contributors there can help you out.