📜 ⬆️ ⬇️

F # practice

Introduction


Perhaps the two most frequently asked (and therefore, vital) questions in the comments to my review article about F # were the following:
1. Why is it so similar to OCaml?
2. Why the hell did he give up?
The answer to the first question is not particularly difficult - it is so similar to OCaml, because it is made entirely from it. Is it good or bad? Yes, rather well, this is clearly better than coming up with a completely new syntax that is not yet known how good it will be. Plus, there is a lot of documentation on OCaml, so even at first, there shouldn't be any problems with (self) learning.
The second question is much more difficult to figure out, especially now that the language is in a beta state and is so far only the object of study of overly inquisitive programmers. However, despite a rather brief acquaintance with him, I have already had occasion to apply him once to achieve quite pragmatic goals, and I will tell you about this in a small post.
I would like to make a reservation in advance, of course, not the last thing that prompted me to solve the task set in F # - the desire to practice in a new language. Of course, the program could also be written in C #, and perhaps it would have turned out to be slightly longer (I repeat, maybe I didn’t check it). Anyway, the program was written, and it did its job.

Problem


A small company where I work is developing various information support systems for domestic submarines. Orders are one-time, very specific, so that until now we have never had problems inherent in product development. For example, with localization. However, it so happened that foreign clients of the Russian defense industry quite unexpectedly had a desire to deploy our system on some export ships. I will not discuss on the topic “Should I have foreseen such an opportunity in advance?” Well, let's say it was necessary, only this does not apply to the topic.
Our application must be said to include more than hundreds of diverse forms written in XAML, dedicated to various design tasks, information windows, etc., arranged in a dozen projects and subfolders in them. And oh, horror, Russian lines were generously scattered evenly over them all. (As it turned out a little later, there were about 1000 lines). And with this it was necessary to do something.

Decision


First of all, I refused the technology promoted by Microsoft almost immediately, because on the one hand it is quite complex (all these satellite assemblies scattered in folders, the need for all components to assign an id, and not a very clear usage model). On the other hand, its capabilities, mainly the ability to switch the language in real time, were absolutely useless in this situation, since it is necessary to get only one copy in another language, and the Vietnamese sailors are unlikely to urgently need its Russian-language counterpart on the ship.
So in the end it was decided to make everything much easier - to put all the lines in the ResourceDictionary, which then merge with the main dictionary located in App.xaml, and in the forms to put them as a StaticResource. So , in general.
The F # program, which parses all xaml files in search of Russian strings, changes them and also creates a separate file for the dictionary, I wrote in less than an hour, it takes less than a hundred lines, along with comments and my passion, every next function in the pipeline, whatever Little she was, write in a new line. And she processed all the files in just over a second. I’ll mention something about speed later.
At first I thought alternately to tell about each method, but then I decided to lay out the entire text, so that you could read the code yourself for the sake of interest, and decide how difficult it is to read the functional code from a sheet. And by the way, contrary to the conventional wisdom, that FWs are suitable for those who want to think a lot, but not enough to write, this particular program didn’t make me think much. Everything happened, as our western brothers like to say straightforward, that is, head to head.
In general, here is the code:
#light
open System
open System.Xml
open System.IO
open System.Collections

let mutable i = 0 //

// xml ,
let rec nodes (node:XmlNode) =
seq { if (node.NodeType <> XmlNodeType.Comment) then yield node
if (node.Attributes <> null) then
for attr in node.Attributes do yield attr
for child in node.ChildNodes do yield! (nodes child)}

// XAML
let rec xamlFiles dir filter =
seq { yield! Directory.GetFiles(dir, filter)
for subdir in Directory.GetDirectories(dir) do yield! xamlFiles subdir filter}

//
let writeXml (doc:XmlDocument) (file:string) =
let xtw = new XmlTextWriter(file, null)
xtw.Formatting <- Formatting.Indented
doc.WriteContentTo(xtw)
xtw.Close()

//
let needLocalize (node:XmlNode) =
let isRussian = Seq.exists ( fun ch -> match ch with
| '' .. '' | '' .. '' -> true
|_ -> false)
node.Value <> null && isRussian node.Value

// , . (string*string) option
let localizeNode (node:XmlNode) =
if (needLocalize node) then
let oldValue = node.Value.Trim()
i <- i+1
let key = "Title_" + i.ToString()
let newValue = sprintf "{StaticResource %s}" key
match node.NodeType with
|XmlNodeType.Element -> (node :?> XmlElement).SetAttribute( "Content" , newValue)
node.Value <- null
|XmlNodeType.Text -> (node.ParentNode :?> XmlElement).SetAttribute(" Content" , newValue)
node.Value <- null
|_ -> node.Value <- newValue
Some(key, oldValue)
else None

// XAML. (, )
let localizeXaml (file:string) =
let doc = new XmlDocument()
doc.Load(file)
let rusDict = nodes doc
|> Seq.to_list
|> List.choose localizeNode //map, Some
File.Copy(file,file+".tmp",true)
writeXml doc file
rusDict

//
let addResource (doc:XmlDocument) (key, value) =
let elm = doc.CreateElement( "system" , "String" , "clr-namespace:System;assembly=mscorlib" )
elm.SetAttribute( "Key" , "http://schemas.microsoft.com/winfx/2006/xaml" ,key)|>ignore
elm.AppendChild(doc.CreateTextNode(value))|> ignore
doc.FirstChild.AppendChild(elm) |> ignore


// XAML
let localizeDirectory dir =
let dict = // , namespaces
let tmp = new XmlDocument()
let fst = tmp.CreateElement( "" , "ResourceDictionary" , "http://schemas.microsoft.com/winfx/2006/xaml/presentation" )
fst.SetAttribute( "xmlns:system" , "clr-namespace:System;assembly=mscorlib" )
fst.SetAttribute( "xmlns:x" , "http://schemas.microsoft.com/winfx/2006/xaml" )
tmp.AppendChild(fst) |> ignore
tmp
xamlFiles dir "*.xaml"
|> Seq.to_list
|> List.filter ( fun file -> int (File.GetAttributes(file) &&& FileAttributes.ReadOnly) = 0)
|> List.map ( fun x -> async {return localizeXaml x})
|> Async.Parallel
|> Async.Run
|> Array.to_list
|> List.iter ( fun lst -> List.iter (addResource dict) lst)
writeXml dict "dict.xml"

//
localizeDirectory Environment.CurrentDirectory


I think it is useful to pay attention to two functions that use list-initialization technology - for xml nodes and file names, an example of one of which I just quoted in a review article. Also of interest, I think, is the LocalizeNode function, which returns the so-called option value. This is an analogue of the nullable type, which has two options Some (value) if some value is given, and None if there is no value. This type is used in the List.concat function, which is similar to List.map, except that it accepts a mapping function that returns an option type (string * string option in this case) and adds only Some values ​​to the final list. Essentially automatically adds List.filter to List.map ( fun i -> i <> None).
In addition, note that in the main function localizeDirectory, the processing of all files is parallelized to all available cores on the computer, which allows you to make a 100% load on the computer and obviously reduce the running time. All that is needed is three gestures and no ThreadPools, not to mention monitors with other semaphores.
On the other hand, the program is interesting (and just specific for F #) in that it actively uses the CLR, in this case XmlDocument, XmlNode and other classes from System.Xml. It is in this that I see at the moment its main advantage over other functional languages.
Well, in general, that's all. I understand, not God knows that, of course, but maybe on this simple example someone can make a conclusion for themselves about the prospects or absence thereof in F #.

')

Source: https://habr.com/ru/post/56004/


All Articles