Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Is It That Some FAKE Methods Don't Work Within Target Functions?

Tags:

target

f#

f#-fake

Consider the following:

#r @"FakeLib.dll"

open Fake
open Fake.StringHelper
open Fake.ProcessHelper

Shell.Exec("mkdir","exampleDirectory")

Target "DoStuff" ( fun () -> 
    trace "Doing Stuff..."
)

Target "Clean" ( fun () -> 
    trace  "Cleaning..."
)

Target "Deploy" (fun () -> 
    trace  "Deploying..."
)

"DoStuff"
    ==>"Clean"
    ==>"Deploy"

RunTargetOrDefault "Deploy"

The above script works fine, but when I move the Shell.Exec within a Target like this:

#r @"FakeLib.dll"

open Fake
open Fake.StringHelper
open Fake.ProcessHelper

Target "DoStuff" ( fun () -> 
    trace "Doing Stuff..."
    Shell.Exec("mkdir","exampleDirectory")
)

Target "Clean" ( fun () -> 
    trace  "Cleaning..."
)

Target "Deploy" (fun () -> 
    trace  "Deploying..."
)

"DoStuff"
    ==>"Clean"
    ==>"Deploy"

RunTargetOrDefault "Deploy"

I end up with a an error:

FsiEvaluationException:

Error:

        DeployScript.fsx(10,5): error FS0001: This expression was expected to have type
            unit
        but here has type
            int


Output: [Loading C:\Users\myusername\Desktop\SomeFolder\DeployScript.fsx]


Input: C:\Users\myusername\Desktop\SomeFolder\DeployScript.fsx
\Arguments:
  C:\fsi.exe

Exception: Yaaf.FSharp.Scripting.FsiEvaluationException: Error while compiling or executing fsharp snippet. ---> System.Exception: Operation failed. The error text has been print the error stream. To return the corresponding FSharpErrorInfo use the EvalInteractionNonThrowing, EvalScriptNonThrowing or EvalExpressionNonThrowing
   at Microsoft.FSharp.Compiler.Interactive.Shell.FsiEvaluationSession.commitResult[a,b](FSharpChoice`2 res)
   at Microsoft.FSharp.Compiler.Interactive.Shell.FsiEvaluationSession.EvalScript(String filePath)
   at [email protected](String arg00) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1303
   at [email protected](Unit unitVar0) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1277
   at Yaaf.FSharp.Scripting.Helper.consoleCapture[a](TextWriter out, TextWriter err, FSharpFunc`2 f) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1221
   at Yaaf.FSharp.Scripting.Helper.redirectOut@1247[a](Boolean preventStdOut, OutStreamHelper out, OutStreamHelper err, FSharpFunc`2 f) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1254
   at [email protected](String text) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1276
   --- End of inner exception stack trace ---
   at [email protected](String text) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1284
   at Yaaf.FSharp.Scripting.Helper.session@1306.Yaaf-FSharp-Scripting-IFsiSession-EvalScriptWithOutput(String ) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1308
   at Fake.FSIHelper.runScriptUncached(Boolean useCache, String scriptPath, IEnumerable`1 fsiOptions, Boolean printDetails, CacheInfo cacheInfo, TextWriter out, TextWriter err) in C:\code\fake\src\app\FakeLib\FSIHelper.fs:line 471
DeployScript.fsx(10,5): error FS0001: This expression was expected to have type

The point here is I'm trying to do a shell command only under a certain target. Pay no attention to the mkdir command as I actually would like to use the schtasks command. I just used mkdir for simplicity purposes. Maybe there's a nuance that I'm missing here. I've also noticed this same behavior with using environVarOrFail. Thanks in advance to all who reply.

like image 460
Leroy Jenkins Coder Avatar asked Mar 08 '23 16:03

Leroy Jenkins Coder


1 Answers

Shell.Exec returns an int, but Target expects a function that returns a unit.

Try this:

let returnUnit() = ()
let return42() = 42

Target "One" returnUnit // works
Target "Two" return42 // fails: expected to have type unit, but here has type int

The target "Two" fails to compile, because its second argument is a function that returns an int, whereas a function returning unit is expected.

This error is there to keep you from forgetting function return values: the compiler helpfully tells you "Hey, are you sure you don't want to do anything with this int? Then why did you call the function in the first place?"

Sometimes, however, it makes sense to throw away the return value. For example, in this particular case, you're calling Shell.Exec for its side effect, and its return value does not interest you. This situation occurs surprisingly often, so often that there is a special function for it - ignore.

Target "DoStuff" ( fun () -> 
    trace "Doing Stuff..."
    Shell.Exec("mkdir","exampleDirectory") |> ignore // compiles fine
)

This function takes any value, throws it away, and returns a unit. By applying it to the result of Shell.Exec, you tell the compiler: "Yes, I'm sure I don't need this value, please ignore it."


That said, I would strongly recommend using specialized functions instead of generic system calls. This will make your script more readable, and might even help you catch an occasional error early. For example, to create a directory, use FileUtils.mkdir:

Target "DoStuff" ( fun () -> 
    trace "Doing Stuff..."
    FileUtils.mkdir "exampleDirectory"  // no need for `ignore`, mkdir already returns `unit`
)
like image 181
Fyodor Soikin Avatar answered Apr 29 '23 18:04

Fyodor Soikin