I'm working on a project and I want to create a really compact method for creating Entities and Attributes.
I want to do this with the pipeline operator. But I want to add extra functionality to this operator.
Like for example :
let entity = (entity "name")
|>> (attribute "attr" String)
|>> (attribute "two" String)
In this example |>> would be a pipeline operator together with the functionality to add an attribute to the entity.
I know that this works:
let entity = (entity "name")
|> addAttr (attribute "attr" String)
So what I want to know is, if it's possible to replace
|> addAttr
with
|>>
Thanks for the help
(I don't know if this is even possible)
You can simply define it like this:
let (|>>) e a = e |> addAttr a
For readability, I would strongly discourage adding custom operators when a simple function will do. You could change the way addAttr is written to make it easier to use in a pipeline:
let addAttr name attrType entity = () // return an updated entity
let e =
entity "name"
|> addAttr "attr" String
|> addAttr "two" String
Related
I am not a functional programmer.
I am learning F#.
I got a problem here.
Let me start from following piece of code:
type XmlNode(tagName, innerValue) =
member this.TagName = tagName
member this.InnerValue = innerValue
member this.Atts = Dictionary<string, obj>()
I don't use F# dict because (as I know) that one is readonly, however I obviously need to modify my attributes.
So I am really struggling to make it pure functional way:
type XmlNode with member this.WriteTo (output:StringBuilder) =
output.Append("<" + this.TagName) |> ignore
//let writeAtts =
// List.map2 (fun key value -> " " + key + "=" + value.ToString())
(List.ofSeq this.Atts.Keys) (List.ofSeq this.Atts.Values)
// |> List.reduce (fun acc str -> acc + " " + str)
//output.Append((writeAtts)) |> ignore
output.Append(">" + this.InnerValue + "</" + this.TagName + ">") |> ignore
output
The code I commented out was my (probably stupid) attemp to use mapping and reduction to concat all the atts in the single correctly formatted string. And that compiles OK.
But when I try to access my Atts property:
[<EntryPoint>]
let main argv =
let root = new XmlNode("root", "test")
root.Atts.Add("att", "val") // trying to add a new KVP
let output = new StringBuilder()
printfn "%O" (root.WriteTo(output))
Console.ReadLine()|>ignore
0 // return an integer exit code
...new attribute does not appear inside the Atts property, i.e. it remains empty.
So:
1) help me to make my code more functional.
2) and to understand how to deal with modificable dictionaries in F#.
Thank you.
First, your immediate problem: the way you defined the Atts property, it's not one value that is "stored" somewhere and is accessible via property. Instead, your definition means "every time somebody reads this property, create a new dictionary and return it". This is why your new attribute doesn't appear in the dictionary: it's a different dictionary every time you read root.Atts.
To create a property with a backing field and initial value, use member val:
type XmlNode(...) =
...
member val Atts = Dictionary<string,object>()
Now, answers to some implied questions.
First order of business: "modify the attributes" and "purely functional" are contradictory ideas. Functional programming implies immutable data. Nothing changes ever. The way to advance your computation is to create a new datum at every step, without overwriting the previous one. This basic idea turns out to be immensely valuable in practice: safer threading, trivial "undo" scenarios, trivial parallelization, trivial distribution to other machines, and even reduced memory consumption via persistent data structures.
Immutability is a very important point, and I urge you not to glance over it. Accepting it requires a mental shift. From my own (and other people I know) experience, it is very hard coming from imperative programming, but it is well worth it.
Second: do not use classes and properties. Technically speaking, object-oriented programming (in the sense of message passing) is not contradictory to functional, but the Enterprise flavor that is used in practice and implemented in C++, Java, C# et al., is contradictory, because it emphasizes this idea that "methods are operations that change an object's state", which is not functional (see above). So it's better to avoid object-oriented constructs, at least while you're learning. And especially since F# provides much better ways to encode data:
type XmlNode = { TagName: string; InnerValue: string; Atts: (string*string) list }
(notice how my Atts is not a dictionary; we'll come to this in a bit)
Similarly, to represent operations on your data, use functions, not methods:
let printNode (node: XmlNode) = (* we'll come to the implementation later *)
Third: why do you say that you "obviously" need to modify the attributes? The code you've shown does not call for this. For example, using my definition of XmlNode above, I can rewrite your code this way:
[<EntryPoint>]
let main argv =
let root = { TagName = "root"; InnerValue = "test"; Atts = ["att", "val"] }
printfn "%s" (printNode root)
...
But even if that was a real need, you shouldn't do it "in place". As I've described above while talking about immutability, you should not mutate the existing node, but rather create a new node that differs from the original one in whatever way you wanted to "modify":
let addAttr node name value = { node with Atts = (name, value) :: node.Atts }
In this implementation, I take a node and name/value of an attribute, and produce a new node whose Atts list consists of whatever was in the original node's Atts with the new attribute prepended.
The original Atts list stays intact, unmodified. But this does not mean twice the memory consumption: because we know that the original list never changes, we can reuse it: we create the new list by only allocating memory for the new item and including a reference to the old list as "other items". If the old list was subject to change, we couldn't do that, we would have to create a full copy (see "Defensive Copy"). This strategy is known as "Persistent Data Structure". It is one of the pillars of functional programming.
Finally, for string formatting, I recommend using sprintf instead of StringBuilder. It offers similar performance benefits, but in addition provides type safety. For example, code sprintf "%s" 5 will not compile, complaining that the format expects a string, but the final argument 5 is a number. With this, we can implement the printNode function:
let printNode (node: XmlNode) =
let atts = seq { for n, v in node.Atts -> sprintf " %s=\"%s\"" n v } |> String.concat ""
sprintf "<%s%s>%s</%s>" node.TagName atts node.InnerValue node.TagName
For reference, here's your complete program, rewritten in functional style:
type XmlNode = { TagName: string; InnerValue: string; Atts: (string*string) list }
let printNode (node: XmlNode) =
let atts = seq { for n, v in node.Atts -> sprintf " %s=\"%s\"" n v } |> String.concat ""
sprintf "<%s%s>%s</%s>" node.TagName atts node.InnerValue node.TagName
[<EntryPoint>]
let main argv =
let root = { TagName = "root"; InnerValue = "test"; Atts = ["att", "val"] }
printfn "%s" (printNode root)
Console.ReadLine() |> ignore
0
I am trying to find all posts in RavenDB containing a word (index is there)
Here is a query that works, finds everything that starts with 'Liv'
let post = query {
for post in session.Query<MyType>() do
where (post.Text.StartsWith("Liv"))
select post
}
An attempt to use string.Contains() method as condition of Where
closure, will throw NotSupportedException.
Here
So I am trying to use Search method where:
Expression<Func<T, object>> fieldSelector,
// Expression marking a field in which terms should be looked for.
C# equivalent from docs:
List<User> users = session
.Query<User>("Users/ByNameAndHobbies")
.Search(x => x.Name, "Adam")
.Search(x => x.Hobbies, "sport")
.ToList();
My first try was to go with
let x = session.Query<MyType>(index).Search((fun xe -> xe.Text ), "Liv")
But getting error because it expects object out. Tried to downcast String to Object (what a strange idea), but getting:
Cannot understand how to translate x => x.Invoke(xe)
At the moment, I am out of ideas. I am supposed to mark field for search and return object. Any ideas?
Thank you.
EDIT 1:
My expression. Gets runtime InvalidCastException because it can't cast string to obj.
let expr =
<# Func<MyType, _>(fun xe -> xe.Text ) #>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<System.Linq.Expressions.Expression<Func<MyType, _>>>
You mentioned that you tried casting the string to object. I tried it using :> obj and it does work.
Here is my working query:
let expr = <# Func<MyType,_>(fun x -> x.Text :> obj ) #>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Linq.Expressions.Expression<Func<MyType,_>>>
let events = session.Query<MyType>()
.Search(expr, "Liv*", decimal 1, SearchOptions.Or, EscapeQueryOptions.AllowAllWildcards)
|> List.ofSeq
I need a data structure for the following:
In a device that has memory slots, each of the slots has a set of parameters. These parameters have different types. The list of possible parameters is fixed, so there is no need for generic flexibility à la »Support of arbitrary parameters without change«. Also, for each parameter, the structure of the contents is known. Typical use cases are the retrieval and modification of one specific parameter as well as a transformation of the complete parameter set into a different (but already defined) data structure.
The natural choice of F# data structure would be a sum type like this:
type SomeParameterContentType = { Field1 : string, Field2 : int }
type SomeOtherParameterContentType = { Other1 : bool option, Other2 : double }
type Parameter =
| SomeParameter of SomeParameterContentType
| SomeOtherParameter of SomeOtherParameterContentType
This way I could create a set and store the parameters there with a very nice data structure. The question here is: Given this idea, how would looking for a specific parameter look like? I don't know of any way to specify a predicate for a find-function for sets. It would be possible to define another sum type listing just the Parameter Types without their contents using this as key for a Dictionary but I don't like this idea too much. Using strings instead of the second sum type doesn't make things better as it still would require providing the list of possible parameters twice.
Does anyone have a better idea?
Thx
--Mathias.
Sounds like all you want is a tryFind for a Set:
module Set =
let tryFind p =
Set.toList >> List.tryFind p
Usage:
let mySet = Set.ofList [1;2;3;4;5]
let m = mySet |> Set.tryFind (fun t -> t = 2)
val m : int option = Some 2
Usage with your Types:
let yourSet = Set.ofList [SomeParameter {Field1="hello";Field2=3}]
let mYours = yourSet |> Set.tryFind (fun t -> match t with
|SomeParameter p -> true
|SomeOtherParameter p -> false)
val mYours : Parameter option = Some (SomeParameter {Field1 = "hello";
Field2 = 3;})
I want to use this function let CopyDir target source filterFile ...(line 219) and specify a filter. The idea is that the filter will contains files which will be excluded. Right now I am using a string value log4net and it is working but I want to replace it with nugetDependencies which is a collection of strings. Would you help me pls
let nugetDependencies = getDependencies "./packages.config"
let excludeNuget (path : string) = path.Contains "log4net" |> not
CopyDir nugetToolsDir (buildDir ## package) excludeNuget
UPDATE:
Fixed wrong URL
I had to read the question a few times to understand it. My understand is that you want to filter a list of file paths by a list of exclusions--with "log4net" being an example of an exclusion.
I'd go something like this, taking advantage of List.exists:
let excludePaths (pathsToExclude : string list) (path: string) =
pathsToExclude |> List.exists (fun exPath -> path.Contains(exPath)) |> not
This implementation can actually curry the labda function fun exPath -> path.Contains(exPath) into simply path.Contains since the method takes a single argument, which would give us:
let excludePaths (pathsToExclude : string list) (path: string) =
pathsToExclude |> List.exists path.Contains |> not
Currying (the F# formal term is partial application) can also be put to use here to bind an argument to the function. To create a check for "log4net", you can simply do this:
let nugetExclusions = ["log4net"]
let excludeNuget = excludePaths nugetExclusions
Just add all of the nuget paths you need to exclude from the list.
Since you are comparing paths contains doesn't have a case-insensitive overload. At least not out of the box. You can add an extension function to string, though. A C# implementation is here on SO.
Here's a F# implementation of the extension method (note that I made this with a small-case contains--F# functions and overloads don't mix):
type System.String with
member x.contains (comp:System.StringComparison) str =
x.IndexOf(str,comp) >= 0
With this type extension in place we can change the excludePaths function to this (again, I'm currying the newly created contains extension method:
let excludePaths (pathsToExclude : string list) (path: string) =
pathsToExclude
|> List.exists (path.contains StringComparison.OrdinalIgnoreCase))
|> not
I hope you continue to use F#.
How about this?
let excludeNuget (path : string) =
nugetDependencies
|> Seq.exists (fun x -> path.Contains x)
so i have got a type Genre with Property Name on it.
Im creating a list of Genre Names like below.
let genres = new Genre()
[ genres.Name <- "Disco";
genres.Name <- "Jazz";
genres.Name <- "Rock"; ] |>ignore
Wondering if there is more succint way of creating this ?.
The code in your example creates just a single Genre object and then creates a list of unit values. A unit value is a bit like void in C# - it is the result of perfroming an expression that does not return anything, but has a side-effect. In your case, the side-effect is modifying the Name property of the single instance (that's what the <- operator does), so you end with a single genres value whose Name is "Rock".
There are numerous ways to change the code to do what you want - to start with what you wrote:
let genre = new Genre()
genre.Name <- "Disco"
This creates a single genre value, so you could create values genre1, genre2 and genre3 and then turn them into a list using:
let genres = [ genre1; genre2; genre3 ]
That would be quite a lot of repetition. If you have a default constructor for Genre that takes the name, you can just write Genre("Disco"). If you don't, you can use F# object initialization syntax and specify the value of the property during the construction as Genre(Name="Disco"). Note you can also omit new if the object does not implement IDisposable.
Now you can construct a list like this:
let genres = [ Genre(Name="Disco"); Genre(Name="Jazz"); Genre(Name="Rock") ]
Now, you can start using functional features like List.map (as suggested by Daniel) or F# list comprehension syntax to make the construction even shorter. In this case, I would probably prefer list comprehension and I'd write:
let genres = [ for name in ["Disco"; "Jazz"; "Rock"] -> Genre(Name = name) ]
This does the same thing as List.map, but using an F# syntax that has been designed for this purpose.
EDIT: Aside, using mutable properties in F# is not always the best way to go. You could try solving the same problem using F# records, which give you an easy way to create copies with modified properties. For example:
// A genre has a name and an era
type Genre = { Name : string; Era : string; }
// Create a template with the basic properties set
let template = { Name = "Default"; Era = "Twentieth century" }
// Create a list of 20th century genres
let genres = [ { template with Name = "Disco" }
{ template with Name = "Jazz" }
{ template with Name = "Rock" } ]
Unlike in the previous case, records are immutable and so you don't risk the confusion that is caused when you create a mutable object and then mutate it. Here, you get a list of three different objects (that are created by copying the template).
["Disco"; "Jazz"; "Rock"]
|> List.map (fun name -> Genre(name))
I think the simplest way would be to use a construcotr which did the assignment for you, then you could write
let genres = Genre("Disco")::Genre("Jazz")::Genre("Rock")::[]
Slightly more terser:
type Genre = Genre of string
let genres = List.map Genre ["Disco"; "Jazz"; "Rock"]
printfn "%A" genres
Prints [Genre "Disco"; Genre "Jazz"; Genre "Rock"].