snippetMinor
How can I make this F# more functional?
Viewed 0 times
thiscanmakemorehowfunctional
Problem
I am currently learning myself a little F# and have written the following code to practice.
The code uses the Mono.Cecil library to inject some IL at the start of every method in each .net assembly found in a given directory. The IL will call a LogMe method in a Loggit.dll assembly.
I want to make this code as functional and idiomatically F# as possible.
My main concern is that the method to inject code into an assembly has some definite side effects. This is unavoidable as thats what the whole point is. I do this in the function InjectMethods. This function is passed to the DoOnModule function.
This DoOnModule function is used twice -
I feel a little uneasy about this double use, which doesnt feel very functional - and yet I find it very useful save code repetition...
How can I make this code more functional?
```
//
open System.IO
open Mono.Cecil
open Mono.Cecil.Cil
exception LogNotFoundError of string
let IsClass(t:TypeDefinition) =
t.IsAnsiClass && not t.IsInterface
let UsefulMethods(t:TypeDefinition) =
t.Methods |> Seq.filter ( fun m -> m.HasBody )
// Perform the given funtion on the module found at the given filename.
let DoOnModule fn (file:string) =
try
let _module = ModuleDefinition.ReadModule file
Some ( fn ( _module ) )
with
| :? System.BadImageFormatException as ex -> printfn "%A" ex; None
| :? System.Exception as ex -> printfn "%A" ex; None
// Do the given function on the dll filenames found in the given directory
let MapAssemblies fn directory =
System.IO.Directory.GetFiles(directory) |>
Seq.filter(fun file -> file.EndsWith("dll") || file.EndsWith("exe") ) |>
Seq.map( fn )
// Return the methods found in the given module
let GetMethods(_module:Module
The code uses the Mono.Cecil library to inject some IL at the start of every method in each .net assembly found in a given directory. The IL will call a LogMe method in a Loggit.dll assembly.
I want to make this code as functional and idiomatically F# as possible.
My main concern is that the method to inject code into an assembly has some definite side effects. This is unavoidable as thats what the whole point is. I do this in the function InjectMethods. This function is passed to the DoOnModule function.
This DoOnModule function is used twice -
- once when injecting methods - a method with side effects and no real return value,
- and once when retreiving the LogMe method reference from the Loggit.dll assembly - a method with no side effects and a useful return value.
I feel a little uneasy about this double use, which doesnt feel very functional - and yet I find it very useful save code repetition...
How can I make this code more functional?
```
//
open System.IO
open Mono.Cecil
open Mono.Cecil.Cil
exception LogNotFoundError of string
let IsClass(t:TypeDefinition) =
t.IsAnsiClass && not t.IsInterface
let UsefulMethods(t:TypeDefinition) =
t.Methods |> Seq.filter ( fun m -> m.HasBody )
// Perform the given funtion on the module found at the given filename.
let DoOnModule fn (file:string) =
try
let _module = ModuleDefinition.ReadModule file
Some ( fn ( _module ) )
with
| :? System.BadImageFormatException as ex -> printfn "%A" ex; None
| :? System.Exception as ex -> printfn "%A" ex; None
// Do the given function on the dll filenames found in the given directory
let MapAssemblies fn directory =
System.IO.Directory.GetFiles(directory) |>
Seq.filter(fun file -> file.EndsWith("dll") || file.EndsWith("exe") ) |>
Seq.map( fn )
// Return the methods found in the given module
let GetMethods(_module:Module
Solution
I only have two minor nitpicks with this code:
Perhaps this should say
This will take the first type whose name happens to contain
Seq.filter(fun file -> file.EndsWith("dll") || file.EndsWith("exe") ) |>Perhaps this should say
".dll" and ".exe". Or you could use Path.GetExtension.Seq.filter ( fun t -> t.Name.Contains ( "Log" ) ) |> Seq.headThis will take the first type whose name happens to contain
Log; even if the name is ILoggable or LogProcessor or something. Are you sure you know that there will never be any other types with such names than the type you want? The same goes for the method LogMe.Code Snippets
Seq.filter(fun file -> file.EndsWith("dll") || file.EndsWith("exe") ) |>Seq.filter ( fun t -> t.Name.Contains ( "Log" ) ) |> Seq.headContext
StackExchange Code Review Q#991, answer score: 4
Revisions (0)
No revisions yet.