Movatterモバイル変換


[0]ホーム

URL:


Header menu logoF# Compiler Guide

Interactive Service: Embedding F# Interactive

This tutorial demonstrates how to embed F# interactive in your application. F# interactiveis an interactive scripting environment that compiles F# code into highly efficient IL codeand executes it on the fly. The F# interactive service allows you to embed F# evaluation inyour application.

NOTE: There is a number of options for embedding F# Interactive. The easiest one is to use thefsi.exe process and communicate with it using standard input and standard output. In thistutorial, we look at calling F# Interactive directly through .NET API. However, if you haveno control over the input, it is a good idea to run F# interactive in a separate process.One reason is that there is no way to handleStackOverflowException and so a poorly writtenscript can terminate the host process.Remember that while calling F# Interactive through .NET API,--shadowcopyreferences option will be ignored. For detailed discussion, please take a look atthis thread.NOTE: IfFsiEvaluationSession.Create fails with an error saying thatFSharp.Core.dll cannot be found,add theFSharp.Core.sigdata andFSharp.Core.optdata files. More infohere.

However, the F# interactive service is still useful, because you might want to wrap it in yourown executable that is then executed (and communicates with the rest of your application), orif you only need to execute a limited subset of F# code (e.g. generated by your own DSL).

Starting the F# interactive

First, we need to reference the libraries that contain the F# interactive service:

#r"FSharp.Compiler.Service.dll"openFSharp.Compiler.Interactive.ShellopenFSharp.Compiler.Tokenization

To communicate with F# interactive, we need to create streams that represent input andoutput. We will use those later to read the output printed as a result of evaluating someF# code that prints:

openSystemopenSystem.IOopenSystem.Text// Initialize output and input streamsletsbOut=newStringBuilder()letsbErr=newStringBuilder()letinStream=newStringReader("")letoutStream=newStringWriter(sbOut)leterrStream=newStringWriter(sbErr)// Build command line arguments & start FSI sessionletargv=[|"C:\\fsi.exe"|]letallArgs=Array.appendargv[|"--noninteractive"|]letfsiConfig=FsiEvaluationSession.GetDefaultConfiguration()letfsiSession=FsiEvaluationSession.Create(fsiConfig,allArgs,inStream,outStream,errStream)

Evaluating and executing code

The F# interactive service exposes several methods that can be used for evaluation. The firstisEvalExpression which evaluates an expression and returns its result. The result containsthe returned value (asobj) and the statically inferred type of the value:

/// Evaluate expression & return the resultletevalExpressiontext=matchfsiSession.EvalExpression(text)with|Somevalue->printfn"%A"value.ReflectionValue|None->printfn"Got no result!"

This takes a string as an argument and evaluates (i.e. executes) it as F# code.

evalExpression"42+1"// prints '43'

This can be used in a strongly typed way as follows:

/// Evaluate expression & return the result, strongly typedletevalExpressionTyped<'T>(text)=matchfsiSession.EvalExpression(text)with|Somevalue->value.ReflectionValue|>unbox<'T>|None->failwith"Got no result!"evalExpressionTyped<int>"42+1"// gives '43'

TheEvalInteraction method can be used to evaluate side-effectful operationssuch as printing, declarations, or other interactions that are not valid F# expressions, but can be entered inthe F# Interactive console. Such commands include#time "on" (and other directives),open Systemall declarations and other top-level statements. The codedoes not require;; at the end. Just enter the code that you want to execute:

fsiSession.EvalInteraction"printfn \"bye\""

TheEvalScript method allows to evaluate a complete .fsx script.

File.WriteAllText("sample.fsx","let twenty = 10 + 10")fsiSession.EvalScript"sample.fsx"

Catching errors

EvalExpression,EvalInteraction andEvalScript are awkward if thecode has type checking warnings or errors, or if evaluation fails with an exception.In these cases you can useEvalExpressionNonThrowing,EvalInteractionNonThrowingandEvalScriptNonThrowing. These return a tuple of a result and an array ofFSharpDiagnostic values.These represent the errors and warnings. The result part is aChoice<_,_> between an actualresult and an exception.

The result part ofEvalExpression andEvalExpressionNonThrowing is an optionalFSharpValue.If that value is not present then it just indicates that the expression didn't have a tangibleresult that could be represented as a .NET object. This situation shouldn't actuallyoccur for any normal input expressions, and only for primitives used in libraries.

File.WriteAllText("sample.fsx","let twenty = 'a' + 10.0")letresult,warnings=fsiSession.EvalScriptNonThrowing"sample.fsx"// show the resultmatchresultwith|Choice1Of2()->printfn"checked and executed ok"|Choice2Of2exn->printfn"execution exception:%s"exn.Message

Gives:

executionexception:Operationcouldnotbecompletedduetoearliererror
// show the errors and warningsforwinwarningsdoprintfn"Warning%s at%d,%d"w.Messagew.StartLinew.StartColumn

Gives:

WarningThetype'float'doesnotmatchthetype'char'at1,19WarningThetype'float'doesnotmatchthetype'char'at1,17

For expressions:

letevalExpressionTyped2<'T>text=letres,warnings=fsiSession.EvalExpressionNonThrowing(text)forwinwarningsdoprintfn"Warning%s at%d,%d"w.Messagew.StartLinew.StartColumnmatchreswith|Choice1Of2(Somevalue)->value.ReflectionValue|>unbox<'T>|Choice1Of2None->failwith"null or no result"|Choice2Of2(exn:exn)->failwith(sprintf"exception%s"exn.Message)evalExpressionTyped2<int>"42+1"// gives '43'

Executing in parallel

By default the code passed toEvalExpression is executed immediately. To execute in parallel, submit a computation that starts a task:

openSystem.Threading.TasksletsampleLongRunningExpr="""async {    // The code of what you want to run    do System.Threading.Thread.Sleep 5000    return 10}  |> Async.StartAsTask"""lettask1=evalExpressionTyped<Task<int>>(sampleLongRunningExpr)lettask2=evalExpressionTyped<Task<int>>(sampleLongRunningExpr)

Both computations have now started. You can now fetch the results:

task1.Result// gives the result after completion (up to 5 seconds)task2.Result// gives the result after completion (up to 5 seconds)

Type checking in the evaluation context

Let's assume you have a situation where you would like to typecheck codein the context of the F# Interactive scripting session. For example, you firstevaluate a declaration:

fsiSession.EvalInteraction"let xxx = 1 + 1"

Now you want to typecheck the partially complete codexxx + xx

letparseResults,checkResults,checkProjectResults=fsiSession.ParseAndCheckInteraction("xxx + xx")

TheparseResults andcheckResults have typesParseFileResults andCheckFileResultsexplained inEditor. You can, for example, look at the type errors in the code:

checkResults.Diagnostics.Length// 1

The code is checked with respect to the logical type context available in the F# interactive sessionbased on the declarations executed so far.

You can also request declaration list information, tooltip text and symbol resolution:

// get a tooltipcheckResults.GetToolTip(1,2,"xxx + xx",["xxx"],FSharpTokenTag.IDENT)checkResults.GetSymbolUseAtLocation(1,2,"xxx + xx",["xxx"])// symbol xxx

The 'fsi' object

If you want your scripting code to be able to access the 'fsi' object, you should pass in an implementation of this object explicitly.Normally the one from FSharp.Compiler.Interactive.Settings.dll is used.

letfsiConfig2=FsiEvaluationSession.GetDefaultConfiguration(fsiSession)

Collectible code generation

Evaluating code in using FsiEvaluationSession generates a .NET dynamic assembly and uses other resources.You can make generated code collectible by passingcollectible=true. However, code will onlybe collected if there are no outstanding object references involving types, for exampleFsiValue objects returned byEvalExpression, and you must have disposed theFsiEvaluationSession.See alsoRestrictions on Collectible Assemblies.

The example below shows the creation of 200 evaluation sessions. Note thatcollectible=true anduse session = ... are both used.

If collectible code is working correctly,overall resource usage will not increase linearly as the evaluation progresses.

letcollectionTest()=foriin1..200doletdefaultArgs=[|"fsi.exe""--noninteractive""--nologo""--gui-"|]useinStream=newStringReader("")useoutStream=newStringWriter()useerrStream=newStringWriter()letfsiConfig=FsiEvaluationSession.GetDefaultConfiguration()usesession=FsiEvaluationSession.Create(fsiConfig,defaultArgs,inStream,outStream,errStream,collectible=true)session.EvalInteraction(sprintf"type D = { v : int }")letv=session.EvalExpression(sprintf"{ v = 42 *%d }"i)printfn"iteration%d, result =%A"iv.Value.ReflectionValue// collectionTest()  <-- run the test like this
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Compiler
namespace FSharp.Compiler.Interactive
module Shellfrom FSharp.Compiler.Interactive
namespace FSharp.Compiler.Tokenization
namespace System
namespace System.IO
namespace System.Text
val sbOut: StringBuilder
Multiple items
type StringBuilder = interface ISerializable new: unit -> unit + 5 overloads member Append: value: bool -> StringBuilder + 25 overloads member AppendFormat: provider: IFormatProvider * format: string * arg0: obj -> StringBuilder + 14 overloads member AppendJoin: separator: char * [<ParamArray>] values: obj array -> StringBuilder + 9 overloads member AppendLine: unit -> StringBuilder + 3 overloads member Clear: unit -> StringBuilder member CopyTo: sourceIndex: int * destination: char array * destinationIndex: int * count: int -> unit + 1 overload member EnsureCapacity: capacity: int -> int member Equals: span: ReadOnlySpan<char> -> bool + 1 overload ...
<summary>Represents a mutable string of characters. This class cannot be inherited.</summary>

--------------------
StringBuilder() : StringBuilder
StringBuilder(capacity: int) : StringBuilder
StringBuilder(value: string) : StringBuilder
StringBuilder(capacity: int, maxCapacity: int) : StringBuilder
StringBuilder(value: string, capacity: int) : StringBuilder
StringBuilder(value: string, startIndex: int, length: int, capacity: int) : StringBuilder
val sbErr: StringBuilder
val inStream: StringReader
Multiple items
type StringReader = inherit TextReader new: s: string -> unit member Close: unit -> unit member Peek: unit -> int member Read: unit -> int + 2 overloads member ReadAsync: buffer: char array * index: int * count: int -> Task<int> + 1 overload member ReadBlock: buffer: Span<char> -> int member ReadBlockAsync: buffer: char array * index: int * count: int -> Task<int> + 1 overload member ReadLine: unit -> string member ReadLineAsync: unit -> Task<string> + 1 overload ...
<summary>Implements a <see cref="T:System.IO.TextReader" /> that reads from a string.</summary>

--------------------
StringReader(s: string) : StringReader
val outStream: StringWriter
Multiple items
type StringWriter = inherit TextWriter new: unit -> unit + 3 overloads member Close: unit -> unit member FlushAsync: unit -> Task member GetStringBuilder: unit -> StringBuilder member ToString: unit -> string member Write: value: char -> unit + 4 overloads member WriteAsync: value: char -> Task + 4 overloads member WriteLine: buffer: ReadOnlySpan<char> -> unit + 1 overload member WriteLineAsync: value: char -> Task + 4 overloads ...
<summary>Implements a <see cref="T:System.IO.TextWriter" /> for writing information to a string. The information is stored in an underlying <see cref="T:System.Text.StringBuilder" />.</summary>

--------------------
StringWriter() : StringWriter
StringWriter(formatProvider: IFormatProvider) : StringWriter
StringWriter(sb: StringBuilder) : StringWriter
StringWriter(sb: StringBuilder, formatProvider: IFormatProvider) : StringWriter
val errStream: StringWriter
val argv: string array
val allArgs: string array
type Array = interface ICollection interface IEnumerable interface IList interface IStructuralComparable interface IStructuralEquatable interface ICloneable member Clone: unit -> obj member CopyTo: array: Array * index: int -> unit + 1 overload member GetEnumerator: unit -> IEnumerator member GetLength: dimension: int -> int ...
<summary>Provides methods for creating, manipulating, searching, and sorting arrays, thereby serving as the base class for all arrays in the common language runtime.</summary>
val append: array1: 'T array -> array2: 'T array -> 'T array
val fsiConfig: FsiEvaluationSessionHostConfig
type FsiEvaluationSession = interface IDisposable member AddBoundValue: name: string * value: obj -> unit member EvalExpression: code: string -> FsiValue option + 1 overload member EvalExpressionNonThrowing: code: string -> Choice<FsiValue option,exn> * FSharpDiagnostic array + 1 overload member EvalInteraction: code: string * ?cancellationToken: CancellationToken -> unit + 1 overload member EvalInteractionNonThrowing: code: string * ?cancellationToken: CancellationToken -> Choice<FsiValue option,exn> * FSharpDiagnostic array + 1 overload member EvalScript: filePath: string -> unit member EvalScriptNonThrowing: filePath: string -> Choice<unit,exn> * FSharpDiagnostic array member FormatValue: reflectionValue: obj * reflectionType: Type -> string member GetBoundValues: unit -> FsiBoundValue list ...
<summary> Represents an F# Interactive evaluation session.</summary>
static member FsiEvaluationSession.GetDefaultConfiguration: unit -> FsiEvaluationSessionHostConfig
static member FsiEvaluationSession.GetDefaultConfiguration: fsiObj: obj -> FsiEvaluationSessionHostConfig
static member FsiEvaluationSession.GetDefaultConfiguration: fsiObj: obj * useFsiAuxLib: bool -> FsiEvaluationSessionHostConfig
val fsiSession: FsiEvaluationSession
static member FsiEvaluationSession.Create: fsiConfig: FsiEvaluationSessionHostConfig * argv: string array * inReader: TextReader * outWriter: TextWriter * errorWriter: TextWriter * ?collectible: bool * ?legacyReferenceResolver: FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver -> FsiEvaluationSession
val evalExpression: text: string -> unit
 Evaluate expression & return the result
val text: string
member FsiEvaluationSession.EvalExpression: code: string -> FsiValue option
member FsiEvaluationSession.EvalExpression: code: string * scriptFileName: string -> FsiValue option
union case Option.Some: Value: 'T -> Option<'T>
val value: FsiValue
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
property FsiValue.ReflectionValue: objnull with get
<summary> The value, as an object</summary>
union case Option.None: Option<'T>
val evalExpressionTyped: text: string -> 'T
 Evaluate expression & return the result, strongly typed
'T
val unbox: value: objnull -> 'T
val failwith: message: string -> 'T
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
member FsiEvaluationSession.EvalInteraction: code: string * ?cancellationToken: Threading.CancellationToken -> unit
member FsiEvaluationSession.EvalInteraction: code: string * scriptFileName: string * ?cancellationToken: Threading.CancellationToken -> unit
type File = static member AppendAllBytes: path: string * bytes: byte array -> unit + 1 overload static member AppendAllBytesAsync: path: string * bytes: byte array * ?cancellationToken: CancellationToken -> Task + 1 overload static member AppendAllLines: path: string * contents: IEnumerable<string> -> unit + 1 overload static member AppendAllLinesAsync: path: string * contents: IEnumerable<string> * encoding: Encoding * ?cancellationToken: CancellationToken -> Task + 1 overload static member AppendAllText: path: string * contents: ReadOnlySpan<char> -> unit + 3 overloads static member AppendAllTextAsync: path: string * contents: ReadOnlyMemory<char> * encoding: Encoding * ?cancellationToken: CancellationToken -> Task + 3 overloads static member AppendText: path: string -> StreamWriter static member Copy: sourceFileName: string * destFileName: string -> unit + 1 overload static member Create: path: string -> FileStream + 2 overloads static member CreateSymbolicLink: path: string * pathToTarget: string -> FileSystemInfo ...
<summary>Provides static methods for the creation, copying, deletion, moving, and opening of a single file, and aids in the creation of <see cref="T:System.IO.FileStream" /> objects.</summary>
File.WriteAllText(path: string, contents: string) : unit
File.WriteAllText(path: string, contents: ReadOnlySpan<char>) : unit
File.WriteAllText(path: string, contents: string, encoding: Encoding) : unit
File.WriteAllText(path: string, contents: ReadOnlySpan<char>, encoding: Encoding) : unit
member FsiEvaluationSession.EvalScript: filePath: string -> unit
val result: Choice<unit,exn>
val warnings: FSharp.Compiler.Diagnostics.FSharpDiagnostic array
member FsiEvaluationSession.EvalScriptNonThrowing: filePath: string -> Choice<unit,exn> * FSharp.Compiler.Diagnostics.FSharpDiagnostic array
union case Choice.Choice1Of2: 'T1 -> Choice<'T1,'T2>
union case Choice.Choice2Of2: 'T2 -> Choice<'T1,'T2>
Multiple items
val exn: exn

--------------------
type exn = Exception
property Exception.Message: string with get
val w: FSharp.Compiler.Diagnostics.FSharpDiagnostic
property FSharp.Compiler.Diagnostics.FSharpDiagnostic.Message: string with get
<summary> Gets the message for the diagnostic</summary>
property FSharp.Compiler.Diagnostics.FSharpDiagnostic.StartLine: int with get
<summary> Gets the start line for the diagnostic</summary>
property FSharp.Compiler.Diagnostics.FSharpDiagnostic.StartColumn: int with get
<summary> Gets the start column for the diagnostic</summary>
val evalExpressionTyped2: text: string -> 'T
val res: Choice<FsiValue option,exn>
member FsiEvaluationSession.EvalExpressionNonThrowing: code: string -> Choice<FsiValue option,exn> * FSharp.Compiler.Diagnostics.FSharpDiagnostic array
member FsiEvaluationSession.EvalExpressionNonThrowing: code: string * scriptFileName: string -> Choice<FsiValue option,exn> * FSharp.Compiler.Diagnostics.FSharpDiagnostic array
val sprintf: format: Printf.StringFormat<'T> -> 'T
namespace System.Threading
namespace System.Threading.Tasks
val sampleLongRunningExpr: string
val task1: Task<int>
Multiple items
type Task = interface IAsyncResult interface IDisposable new: action: Action -> unit + 7 overloads member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredTaskAwaitable + 1 overload member ContinueWith: continuationAction: Action<Task,obj> * state: obj -> Task + 19 overloads member Dispose: unit -> unit member GetAwaiter: unit -> TaskAwaiter member RunSynchronously: unit -> unit + 1 overload member Start: unit -> unit + 1 overload member Wait: unit -> unit + 5 overloads ...
<summary>Represents an asynchronous operation.</summary>

--------------------
type Task<'TResult> = inherit Task new: ``function`` : Func<obj,'TResult> * state: obj -> unit + 7 overloads member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredTaskAwaitable<'TResult> + 1 overload member ContinueWith: continuationAction: Action<Task<'TResult>,obj> * state: obj -> Task + 19 overloads member GetAwaiter: unit -> TaskAwaiter<'TResult> member WaitAsync: cancellationToken: CancellationToken -> Task<'TResult> + 4 overloads member Result: 'TResult static member Factory: TaskFactory<'TResult>
<summary>Represents an asynchronous operation that can return a value.</summary>
<typeparam name="TResult">The type of the result produced by this <see cref="T:System.Threading.Tasks.Task`1" />.</typeparam>


--------------------
Task(action: Action) : Task
Task(action: Action, cancellationToken: Threading.CancellationToken) : Task
Task(action: Action, creationOptions: TaskCreationOptions) : Task
Task(action: Action<obj>, state: obj) : Task
Task(action: Action, cancellationToken: Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task
Task(action: Action<obj>, state: obj, cancellationToken: Threading.CancellationToken) : Task
Task(action: Action<obj>, state: obj, creationOptions: TaskCreationOptions) : Task
Task(action: Action<obj>, state: obj, cancellationToken: Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task

--------------------
Task(``function`` : Func<'TResult>) : Task<'TResult>
Task(``function`` : Func<obj,'TResult>, state: obj) : Task<'TResult>
Task(``function`` : Func<'TResult>, cancellationToken: Threading.CancellationToken) : Task<'TResult>
Task(``function`` : Func<'TResult>, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : Func<obj,'TResult>, state: obj, cancellationToken: Threading.CancellationToken) : Task<'TResult>
Task(``function`` : Func<obj,'TResult>, state: obj, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : Func<'TResult>, cancellationToken: Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : Func<obj,'TResult>, state: obj, cancellationToken: Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
val task2: Task<int>
property Task.Result: int with get
<summary>Gets the result value of this <see cref="T:System.Threading.Tasks.Task`1" />.</summary>
<exception cref="T:System.AggregateException">The task was canceled. The <see cref="P:System.AggregateException.InnerExceptions" /> collection contains a <see cref="T:System.Threading.Tasks.TaskCanceledException" /> object. -or- An exception was thrown during the execution of the task. The <see cref="P:System.AggregateException.InnerExceptions" /> collection contains information about the exception or exceptions.</exception>
<returns>The result value of this <see cref="T:System.Threading.Tasks.Task`1" />, which is of the same type as the task's type parameter.</returns>
val parseResults: FSharp.Compiler.CodeAnalysis.FSharpParseFileResults
val checkResults: FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults
val checkProjectResults: FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults
member FsiEvaluationSession.ParseAndCheckInteraction: code: string -> FSharp.Compiler.CodeAnalysis.FSharpParseFileResults * FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults * FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults
property FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults.Diagnostics: FSharp.Compiler.Diagnostics.FSharpDiagnostic array with get
<summary> The errors returned by parsing a source file.</summary>
property Array.Length: int with get
<summary>Gets the total number of elements in all the dimensions of the <see cref="T:System.Array" />.</summary>
<exception cref="T:System.OverflowException">The array is multidimensional and contains more than <see cref="F:System.Int32.MaxValue">Int32.MaxValue</see> elements.</exception>
<returns>The total number of elements in all the dimensions of the <see cref="T:System.Array" />; zero if there are no elements in the array.</returns>
member FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults.GetToolTip: line: int * colAtEndOfNames: int * lineText: string * names: string list * tokenTag: int * ?width: int -> FSharp.Compiler.EditorServices.ToolTipText
module FSharpTokenTagfrom FSharp.Compiler.Tokenization
<summary> Some of the values in the field FSharpTokenInfo.Tag</summary>
val IDENT: int
<summary> Indicates the token is an identifier (synonym for FSharpTokenTag.Identifier)</summary>
member FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults.GetSymbolUseAtLocation: line: int * colAtEndOfNames: int * lineText: string * names: string list -> FSharp.Compiler.CodeAnalysis.FSharpSymbolUse option
val fsiConfig2: FsiEvaluationSessionHostConfig
val collectionTest: unit -> unit
val i: int32
val defaultArgs: string array
val session: FsiEvaluationSession
val v: FsiValue option
property Option.Value: FsiValue with get

On this page

Type something to start searching.


[8]ページ先頭

©2009-2025 Movatter.jp