|
|
|
|
 Monday, November 03, 2008
Several times I've had to explain a few basic F# things to new users. I ran into some of these too when I first looked at F#. If you've been looking at F#, but some things still don't click, I hope this will help break the chains of C/imperative languages. I'm assuming you know a bit about F#, perhaps from the Quick Tour or the F# Tutorial file that comes with the Visual Studio integration.
Functions always take and receive exactly one argument. Despite anything you see, in F#, there's only one argument, and only one return value. Let's look at how this plays out (I suggest firing up F# interactive and playing around):
> let inc x = x + 1;; val inc : int -> int
> let add x y = x + y;; val add : int -> int -> int
[I like to read -> as "to", because it's short and sweet.] So, for the case of "inc x", we see the signature is quite simple and expected "int to int". But for add? We see "int to int to int". What this actually means is "a function that takes an integer, and returns a function that takes an integer and returns an integer".
The same signature in C# would look like this: Func<int, <Func<int,int>>> or "Func<int,int> Add(int x)". [Some day, think how odd it is to have two ways of representing the function.] So when we call the F# function, we're really passing in the first argument, getting a new function, then applying the second argument. Something like this:
1. add x y 2. (add x) y 3. closure1 y 4. final-result
"closure1" is what we get from the "partial" application of add. I'll touch on this in a minute.
So how do we _really_ pass in two arguments at once? We put two arguments into one value, a tuple. We write it like this:
> let add (x, y) = x + y;; val add : int * int -> int
Notice the new type signature: "int by int to int" or "int int tuple to int". In C#, this would be:
int add(Tuple<int,int> arguments) { return Tuple.A + Tuple.B; }
But F# nicely provides support for tuples [via pattern matching], so we can write them much more naturally. This syntax also works on the way out:
> let pow23 x = x*x, x*x*x;; val pow23 : int -> int * int // "int to int int tuple"
> let square, cube = pow23 7;; val square : int val cube : int
> square, cube;; val it : int * int = (49, 343)
There's never no value Another common typo/misunderstanding is when creating a function with no arguments:
> let sayHi = printfn "Hi";; val sayHi : unit
Hi > sayHi;; val it : unit = ()
Oh, what happened here? Remember how _everything_ takes and returns exactly one value? The same is true of functions that "don't return a value", such as printf. Instead of "not returning a value" like C's void, functions return a special type called unit with one value, ().
Armed with this, let's see what the sayHi definition actually says. It says "let a value called sayHi equal to the result of printfn". Well, since the result of printfn is unit, sayHi becomes unit. The execution happens immediately, and subsequent uses of sayHi just get the value, unit.
To actually do what we want, we need to take an argument. Then F# knows we're a function value:
> let sayHi() = printfn "Hi";; val sayHi : unit -> unit
> sayHi;; val it : (unit -> unit) = <fun:clo@0>
> sayHi();;
Hi
val it : unit = ()
By explicitly taking a unit parameter, sayHi becomes a function (type unit to unit). Notice that if we just write "sayHi", we're just going to get the _value_ of it (a function), not apply (execute) the function.
Partial application OK, so a side effect of this system of "take on param and return a function that takes the next", is that we can compose with just parts of a function. For instance, to write an increment function, we can do this:
> let add x y = x + y;; val add : int -> int -> int
> let inc x = add x 1;; val inc : int -> int
So far, nothing interesting. However, we can also write this more effectively:
> let inc = add 1;; val inc : (int -> int)
Aha! What's happening here is what happens "secretly" each time we call add with 2 parameters. We're just using the intermediate result, the closure of "add 1", and assigning that function value to inc. In C#, it'd be something like this:
public static Func<int, int> Add(int x) {
return y => x + y;
}
public static Func<int, int> Inc = Add(1);
// and "full application" of add looks like this: Add(1)(2)
[BTW, this isn't really currying in F#. Currying is taking a method of type "a' * 'b -> 'c" and turning it into "'a -> 'b -> 'c". Since F# methods are "automatically curried", there's no need for a "curry" step (well, except perhaps when using .NET methods, which are always "tupled", but that's another story).]
[Side note: a function like "add" is superfluous, because in F#, operators are functions: > (+);; val it : (int -> int -> int) = <fun:clo@18> > let inc = (+) 1;; val inc : (int -> int)]
The pipeline
F# appears to have all this complicated syntax, with |> <| >> << and so on. But, these operators are defined in F# code, and follow some basic rules. They aren't magic or have any special compiler support.
The most important function operator is |>. A quick search of the F# source shows: C:\Program Files\FSharp-1.9.6.2\source\fsharp\FSharp.Core\prim-types.fs(2062): let inline (|>) x f = f x
(Operators are surrounded in parentheses to define them and to use them as functions with prefix notation.) The type signature is: > (|>);; val it : ('a -> ('a -> 'b) -> 'b) = <fun:clo@23>
This is "alpha to a function alpha to beta to beta". That probably didn't help. Perhaps looking at the type of function application will help:
> let apply f x = f x;; val apply : ('a -> 'b) -> 'a -> 'b
This demonstrates that a function application is really just taking a function "alpha to beta", giving it an alpha, and getting a beta. [I'm open to suggestions on better ways to pronounce type arguments.]
So, glance back up at the pipeline operator. We can see it's really just function application _in reverse_. What is the use of such a construct? If you've used C# 3.0's LINQ extension methods or a Unix shell, you probably already know. By reversing the function application, we can write things in a much more natural order. To modify something from the F# Quick Tour:
> let filterTypes name = - System.AppDomain.CurrentDomain.GetAssemblies() - |> Seq.map (fun a -> a.GetTypes()) |> Seq.concat - |> Seq.map (fun a -> a.Name) - |> Seq.filter (fun s-> s.Contains name);;
val filterTypes : string -> seq<string>
> filterTypes "Coll";; val it : seq<string> = seq ["ICollection"; "EvidenceCollection"; "GCCollectionMode"; "CollectionBase"; ...] >
Now, this is a bit embarrassing, but this took me a long time to get. I stared at this and re-read the F# manual for probably an hour. How could something so simple do such "complex" stuff?? Once I finally got used to the idea of functions being normal values, and the whole "one arg one value" bit, it snapped together. The other operators (<|, >>, <<) are pretty easy to follow once the basics are understood (going through prim-types.fs is a great experience).
What else? I hope this all helps fit some pieces together. Feel free to use the MSN thing on the side of my site to ask questions or give me suggestions. Thanks!
Edit: Also check out F# function types: fun with tuples and currying (From an F# team member's blog.)
|
|
Code | FSharp
|
Monday, November 03, 2008 9:07:15 PM UTC
|
Trackback
|
 Tuesday, October 28, 2008
Compiling for Silverlight 2 is a bit of a pain still. It's even worse with F#, because the Silverlight project system cannot tell when you're building your F# components correctly (using the right flags). So you get this error:
--------------------------- Microsoft Visual Studio --------------------------- You can only add project references to other Silverlight projects in the solution. --------------------------- OK ---------------------------
This happens even if you set up the F# compiler options correctly by adding: --standalone --noframework --cliroot "C:\program Files\Microsoft Silverlight\2.0.31005.0"
It still happens if you reference the DLL directly (FSLib1\bin\debug), you get the same error! Apparently VS or Silverlight projects go and try to find the project relating to the DLLs when you add a reference. So, the solution is easy: Move the DLL somewhere else. Then you can add a file reference, and it will work just fine.
One more problem. I get an FSC error when I turn optimize code on: C:\test\SilverlightApplication1\FSC(0,0): error FS0193: internal error: the module/namespace 'System' from compilation unit 'mscorlib' did not contain the namespace, module or type 'MarshalByRefObject'
If I leave optimize code off, then it seems to work. Good luck.
http://stackoverflow.com/questions/237044/how-does-silverlight-determine-an-assembly-is-silverlight
|
|
FSharp
|
Tuesday, October 28, 2008 10:27:50 PM UTC
|
Trackback
|
 Monday, October 13, 2008
mod_managed is now in the FreeSWITCH tree. This replaces mod_mono, and allows selection of either the Microsoft CLR or Mono 2.0+ as the runtime engine to use. All the interfaces, and most of the code, are identical for both versions. Modules written on Mono will work on the CLR version and vice versa.
Check it out.
|
|
Code | FreeSWITCH
|
Monday, October 13, 2008 10:43:10 PM UTC
|
Trackback
|
 Friday, October 10, 2008
In this image, we see Banco Industrial (Guatemala) online bank. The Flash applet displays:
System Hours - Monday to Sunday: From 6:00am to 22:00 hours. Last day of month: From 6am to 21:30 hours
WTF? Closing the online bank? By the way, who writes "Monday to Sunday?" And a 30 minute difference on the last day of the month? The levels of incompetence here just stack up, but it's par for the country.
|
|
Guatemala
|
Friday, October 10, 2008 3:13:41 AM UTC
|
Trackback
|
Every now and then I read an article on VoIP security. These articles almost always go over the obvious stuff such as lack of encryption, eavesdropping and ensuring you firewall your networks and so on. While certainly major issues, especially for a corporate deployment, there are still some other interesting issues.
One thing that keeps getting mentioned is the possibility for VoIP peering. Peering allows VoIP providers to send calls directly to each other (possibly over the Internet, maybe over [semi-]private connections). The main idea is cost savings, since the call doesn't need to go out over the public telephone network (PSTN).
To accomplish this, they'll set up a shared database mapping telephone numbers to VoIP providers. So, when a VoIP provider attempts to place a call, it'll consult this directory first. If it finds the number in there, it'll send it direct to the provider instead of over the PSTN. All the providers sign some sort of contract to say they'll be careful with the database and not populate it with invalid entries. Let's just assume the VoIP provider is trustworthy and hires trustworthy people (this is a stupid assumption, but I've had a peering company tell me this, as the security problems are too obvious without this assumption).
This system actually holds true inside of a VoIP provider's own network. A provider will want to terminate directly to a customer instead of out via the PSTN then back into their own network. So they'll probably have a directory of their own numbers so they can route those directly.
Well first off, now every peering member's security is bound by the security of every other member. If just one "trustworthy" peering provider gets compromised (not a hard task - more on that later), they can pollute the shared directory and hijack phone numbers. Being able to redirect a financial institution's phone number sounds like a profitable attack. An attacker can simply route the call to their system, then pass it through to the PSTN to avoid detection by users. Note that none of the security technologies available can prevent problems with a subverted, trusted, directory.
But it gets easier... Many providers let you port your existing number to them when you sign up. From my limited experience, I've seen some of them immediately activate the number for you, so you can get started and going with their network while the port happens. A port can take a bit of time (and for now, let's assume the porting system is secure), so this sounds like a reasonable approach.
Wrong. First off, the new customer's number will probably go right into the provider's internal database, so all calls from that provider will go to the customer attacker. Depending on the size of the provider, this could be a pretty decent attack in and of itself.
But now, suppose the peering contract didn't specify not provisioning ports-in-progress, or if it did, the implementation people messed up. Now ALL the VoIP providers have been compromised, by a single provider who was agressive in their porting tactics.
Eventually it'll probably get resolved, but even a few hours or days of compromising a valuable phone number can be a significant attack.
What's the threat? As a consumer, in general, I'd not worry too much about people trying to tap my line, just like I rarely worry about the safety of my wired Internet connection. But similar to intercepting credit card info versus hacking a company's database, this is a much juicier target. An attacker who pulls this off gets access to bulk information. Thus, I think the threat of something like this happening is much higher than having my individual calls monitored.
|
|
Security | VoIP
|
Friday, October 10, 2008 12:25:14 AM UTC
|
Trackback
|
 Wednesday, September 24, 2008
One common issue with IEnumerables is that you can't find out anything about them until you use them. A frequent scenario is wanting to know if the IEnumerable is empty before you go ahead and use it. For example, you may want to write a result set to a file, but only if there's actually data.
As far as I know, the .NET Framework has no built-in classes to facilitate this, so I hacked up my own. You use it by wrapping an IEnumerable inside an EmptyCheckEnumerable. Then, when you check the IsEmpty property, it gets the enumerator and calls MoveNext once. When you then go to consume it, it intercepts the MoveNext call and simply returns the previous value. From then on, it just passes through. The result is that you don't consume the IE twice, which can be necesary for performance or other reasons.
Example:
var res = new EmptyCheckEnumerable<object>(dc.Execute<object>("SELECT * FROM foo"));
if (!res.IsEmpty) {
// allocate resources and consume the IE - will only execute the SELECT once
}I suggest creating a "ToEmptyCheck" extension method to flow type information. Code: EmptyCheckEnumerable.cs.txt (2.14 KB)
|
|
Code
|
Wednesday, September 24, 2008 10:11:30 PM UTC
|
Trackback
|
 Friday, September 19, 2008
In a previous comment, someone mentioned the OO mindset ("mold" -- quite appropriate). I don't want to go into it much, but simply "quote for win" something from here http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html. It's a nice take on things and I got a kick out of it:
" The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil - objects are merely a poor man's closures."
Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.
On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's object." At that moment, Anton became enlightened.
"
|
|
Code | FSharp | Humour
|
Friday, September 19, 2008 6:00:50 PM UTC
|
Trackback
|
 Thursday, September 18, 2008
FreeSWITCH's mod_mono plugin should now work fine on Linux. Additionally, it's in the Visual Studio Solution file, all you need to do is compile mod_mono (it won't build by default). There's also now support for running mod_mono inside FreeSWITCH inside another managed application, just in case you're embedding FreeSWITCH.
For more info: http://wiki.freeswitch.org/wiki/Mod_mono
|
|
FreeSWITCH
|
Thursday, September 18, 2008 2:24:38 AM UTC
|
Trackback
|
I hacked up a little class to enable us to use SQL 2008's Change Tracking feature with LINQ-to-SQL. Change Tracking allows you to see which keys (and optionally columns) have changed in the database from a specific version. The SQL docs have a great overview with lots of examples and information.
Basically, we get a special CHANGETABLE function to SELECT from, which gives us the change information and keys. Additionally, there is the issue of versioning. Changes are only kept so long, so we want to make sure the last version we sync'd is still compatible, otherwise we have to re-initialize.
Finally, in order for our change SELECTs to be coherent, we need to snapshot the database. The easiest way to get this is by turning on Snapshot Isolation. Snapshot isolation allows us to read a virtual snapshot of the database. Any changes made from when we begin our transactions are not visible to us and we do not lock anything we read.
Here's an excerpt from a class I have to provide change tracking for our database:
public DbDataChangeProvider(long lastVersion) {
this.lastVersion = lastVersion;
this.txScope = ChangeTracking.GetSnapshotScope();
var validV = ChangeTracking.GetValidVersionForAll(dataContext);
baseline = lastVersion < validV;
currentVersion = ChangeTracking.GetCurrentVersion(dataContext);
}We take in the last version, then initialize a SnapshotScope. We get the minimum valid version and see if we're going to have to generate a baseline (re-init) or not. Next, we grab the current version of the database, so consumers can save the version for when they sync up next.
To get changed keys, you can do this: ChangeTracking.GetChangedKeys<string>(dataContext, "Accounts", "AccountName", lastVersion, System.Data.Linq.ChangeAction.Delete);
This will give you an enumeration of all the Deleted keys; use other ChangeActions to get Insert or Updated. There's also a filter (SQL string) to limit further.
To get changed _items_, you can pass in a Queryable, like this: ChangeTracking.GetChangedItems<Account>(dataContext, dataContext.Accounts.Where(a=>a.Balance>10), "Accounts", "AccountName", lastVersion);
The code should (seems to work for me) figure out your query and inject the JOIN to the CHANGETABLE function. The code is linked at the end of this article. Some of the functions use a Tuple type; if you don't have it, I've posted it elsewhere on this site. Or, you can delete those methods; they are only for 2-key tables. ChangeTracking.cs.txt (9 KB)
|
|
Code | Misc. Technology
|
Thursday, September 18, 2008 2:03:46 AM UTC
|
Trackback
|
 Wednesday, September 17, 2008
Something I've heard often is that "F# is too complex/functional programming is too hard". This is something that sorta came up in the comments here: http://www.atrevido.net/blog/2008/09/16/Why+NOT+F.aspx.
Why is this irrelevant? You only learn a language once. You pay the learning curve cost one time; after that, you have the techniques and power at your disposal. However, you pay the cost of code every time you read or write it. Since I'm going to read and write a lot more code than number of languages I learn, I'd much, much, prefer to pay this overhead once, up front, rather than in my code each time.
Of course, it's not necessarily this simple. It's possible to design a powerful language that renders code even more difficult. Regular expressions are perhaps a nice example of this; they're often called "write only" code. Every time I do a non-trivial regexp, I'm always going back to the reference. Another example are C macros -- text-based, they quickly let you get into trouble. The design of C# runs away from this and tries to make it very difficult to write code that is "hard" to figure out -- if there's any edge case where a feature might be confusing or not work, C# tends to not allow it at all.
F# design is different. F# doesn't try to shelter users - it gives you tools and lets you decide how to use them. It makes the assumption that if you're writing a program, *you have some clue of what you're doing*. F#'s tools are still safe (compared to say, C), but sure, you can go create a mess if you'd like.
Poor code quality is *not* something that should be fixed solely via technical measures. I liken it to using web filters to make sure "employees aren't goofing off on the Internet" -- this is a policy/management issue and should be solved via administrative means. If one of my devs is spending all day on 4chan but gets work done and adds value, what do I care? Similarly, if the code quality coming out is acceptable and the solutions work correctly, I don't care if it used macros, custom operators, "difficult" code, etc. The process to make sure code quality is high (code reviews) will take care of anyone abusing language features in stupid ways.
But in truth, F# isn't actually much more complex to use. To the beginner, what seems to be "unnecessary terseness" and a lot of complicated syntax is actually a very basic system in action. Many of the "built-in" F# features such as the |> pipeline operator are defined right in the language itself. There's no magic going on -- you can create your own functionality in exactly the same way. Once you understand the basic rules, you'll see that most everything else follows them.
But at any rate, why is "easy to learn" a benefit? Sure, it's handy to promote a language if people can pick it up easily, but it's not indicitive of long-term power. True, if you have a "web developer" who's going to add a few server-side scripts, it's nice that he doesn't have to learn much. But if you're developing an application of any substance, I fail to see how these help, given the negative effects of having a "simple" language.
P.S. On my site I'm not trying to infer that F# will take over the world or that C# will go away. I've met too many "professional developers" to realise that anything that requires thinking isn't going to achieve stellar adoption. I'm simply pointing out that the reasons come down to apathy and intelligence (with respect to the learning curve; there could be other reasons as well), regardless of how politically correct one phrases it.
|
|
Code | FSharp
|
Wednesday, September 17, 2008 8:49:06 PM UTC
|
Trackback
|
 Tuesday, September 16, 2008
This is actually an open request for comments. I'm honestly interested in hearing why F# is not always the better candidate versus C#. What can C# do well that F# cannot? In nearly everything, F# seems to come out on top, as far as I can see.
Let's get these out of the way:
- Personal preference. Enough said. - In beta. Enough said. - Legacy code. Sure, if you have a project in C#, it may not make too much sense to switch mid-way. - Management. Enough said. - No benefit. This is simply lack of education and needs to be addressed separately. - F#'s too hard/it's hard to hire F# devs. This is a non-issue that is a separate topic. In summary, anyone worth hiring for C# work should be able to handle F#. [Exception being a very small deadline with an existing team...]
The only code reason I've seen is heavy native interop/pointer work. F# seems to be slightly more verbose than C# in this case. It's not much more, but I could see if you're doing just pointer code then it could get annoying. (Interestingly enough, F# COM interop is much nicer than C# because it supports named and optional parameters (http://blogs.msdn.com/dsyme/archive/2008/05/02/full-release-notes-for-f-1-9-4.aspx, search for "chart")...)
What other reasons are there for C# over F#?
Edit: Good point in the comments about C# being a standard with open source implementations. That could be a big issue for some. Another good point is the current lack of tool support (like ClickOnce, ASPX and WPF designers, etc.). I don't see any intrinsic reason F# wouldn't have those, except for limited resources.
|
|
Code | FSharp
|
Tuesday, September 16, 2008 5:06:28 AM UTC
|
Trackback
|
When specifying a connection string to use with LINQ-to-SQL, make sure you "correctly" case MultipleActiveResultSets. If you have something like this: "Server=(local);Database=master;Integrated Security=true;App=dcdemo;MultipleActiveResultsets=true", LINQ-to-SQL will determine MARS is disabled even though it is not.
This is because inside the LINQ-to-SQL implementation, a normal String.Contains is performed on the connection string. Since this is an ordinal compare, it won't find MARS turned on if you spell it differently. Without MARS enabled, if you execute multiple queries on a single DataContext, it will force the outstanding queries to buffer. This means that instead of a nice lazy reading from the SQL server, you'll end up bringing it all into local memory.
More info here: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=366444
|
|
Code
|
Tuesday, September 16, 2008 5:02:00 AM UTC
|
Trackback
|
 Tuesday, September 02, 2008
|
There are other great books out there such as Expert F#.
The F# Dev center has links to many other "learn F#" articles. All of
these are great.
But, something I found helpful is going "purely functional", and
Haskell is the perfect vehicle. When you're forced to think only functional,
and don't have the other "escapes" F# has, you bend your mind into
understanding how you can accomplish things without using mutation or
object-orientation.
The downside of Haskell is that many resources seem to be very challenging to
get into. There's no doubt that the learning curve for Haskell can be tough. On
top of that, many materials tend to dive right into monads and it tends to end
up too scary. I've even bought several other good books on functional
programming, but none of them were easily approachable. (They have good
content, but you can't start from zero by using them.)
Enter Real World Haskell. This
is a *very* easy to follow book and really drives functional programming home.
It doesn’t assume you know anything about functional programming at all, so the
learning curve is a gentle slope.
Even better? It's completely available online, so you can
start reading it right
now! Plus, it has reader-submitted comments which are of tremendous use, as
they ask and answer many common questions that might arise as you read along,
without interfering with the flow. You can read the entire book here: http://book.realworldhaskell.org/read/
[But buy it to support the excellent work the authors have done!]
I've found that my F# skills have gone up tremendously by reading Real World
Haskell. For instance, I "sorta" understood F#'s computation
expressions and builder. Say, enough to use them -- that's easy, like most
things in F#), but understanding the concepts behind them? Starting to learn
Haskell really brings the understanding around. This isn't to say that you'll
eschew mutation and OO in F# -- such concepts can be very useful (and increase
performance on the CLR). But at least you'll know when a more elegant solution
is available.
(Plus, it's fun! As someone in #haskell on freenode put it to me:
"Learning Haskell will f*ck with your brain, and you'll like it.")
|
|
Code | FSharp
|
Tuesday, September 02, 2008 4:00:06 AM UTC
|
Trackback
|
Managed code? In MY softswitch? It's more common than you think.
A while back, I tried creating a mono plugin for Asterisk. The goal being that'd you'd be able to write in-process apps in decent languages, rather than resorting to C. Well, there were no standards of how functions operated (i.e., who's responsible for memory), and my requests to standardize these were met with hostility. (Actually, when I asked why they didn't refactor some stuff, like the multi-thousand-line switch statements, I was told "refactoring doesn't exist for C"... sigh.) But the real showstopper was that we couldn't figure out how to get Asterisk to play nice with mono as far as threads go. I had a very active Asterisk developer work on it with me, and he pretty much told me it wasn't going to work. Oh well.
When I stopped really being involved with Asterisk, I had seen a couple big Asterisk developers leave to start FreeSWITCH. It was mainly an idea at that point, to do things right and build a scalable, clean system. Well, fast forward a few years and FreeSWITCH has really come far. You can go read their site for more details, but it's a more flexible, scalable, and vastly cleaner VoIP platform. That's why, at v1.0.1, it already supports UDP, TCP, TLS, SRTP, IPv6, wideband codecs, and more. Oh, and it runs cross-platform, including Windows.
But the really great thing about FS is that it was designed to be modular from the beginning. And, it was designed to allow people build apps into it. It has some nice out-of-process extensibility, think Asterisk Manager and AGI, without the suck. [I wonder how many * flames I'm gonna get?] But also of interest, they designed an app API from the start. It's written in C++, and SWIG friendly so you can quickly add other languages (Javascript and Lua seem to be the most popular).
Well, the thought of running interpreted stuff in-process disgusted me, so I immediately set out to try to add Mono support. Much to my surprise, the FS team was extremely supportive. They helped me figure out all the bits of FS and the API I needed. In fact, one of the project leads told me, "Hell, if you're gonna write mod_mono, we'll give you checkin support right now.". A short bit later, we had mod_mono working!
Not satisfied with doing just apps, I turned to doing a full integration. A bit of SWIGwork later, and I had the entire FreeSWITCH header files SWIG'd and C# accessible. I found a few APIs that didn't import (varargs stuff, I think), but the FS team helped add some different methods to work around that.
The Mono side was pretty straightforward. We write a C/C++ module for FS that gets Mono up and running. Then it kicks off mod_mono_managed, which actually loads modules, runs initialization code, etc. The biggest problem I ran into was a problem between SWIG and Mono. Mono would attempt to free strings with g_free, crashing on Windows. I wrote a SWIG-C# post-processor to tweak the SWIG code to avoid it; I'm not sure it's accurate, but we haven't noticed issues so far. A few things we ran into were quickly cleared up by the awesome people #mono. This week we'll be testing and make sure it's fixed in Mono 2.0.
So, the bottom line is that you can write any sort of module for FreeSWITCH, from simple apps and API functions, down to a full codec or endpoint, all in managed code (though, the interop might not be pretty sometimes). I've tried some simple stuff out in C#, F#, and Javascript (JScript .NET), and it all works fine. The code is in the FS SVN, under /freeswitch/src/mod/languages/mod_mono and mod_mono_managed. It's not part of the official build yet, so there may be a bit of work involved in getting it going. But I have heard from other people that they've gotten it working. Some info is here: http://wiki.freeswitch.org/wiki/Mod_mono -- Right now, we've only tested on Mono 1.9.1, but we're going to only support 2.0 as of very soon. (F# only supports 2.0, so earlier versions are irrelevant to my interests :).)
This is exciting for us, as it allows us to build *safe* applications that run in-process with the switch. But, unlike the embedded scripting languages, we get top-notch performance and access to complete libraries. We're combining this in-process approach with out-of-process (Event Socket, outbound) to get a robust and very scalable system. For our particular application, we have several GB of decision data that we keep in RAM on separate servers (which is in turn sync'd to SQL Server 2008, via a proprietary change replication system). For obvious reasons, we didn't want that memory being stuck in the FS process. Having Mono on both sides allows us to share code between the different systems, while still getting optimizations for both environments.
You can email me: mgg AT a t r e v i d o _ net or use the chat widget on the on my site if you have questions or problems getting it going.
I'll post a follow up once it's in the official build.
|
|
FreeSWITCH | VoIP
|
Tuesday, September 02, 2008 2:04:38 AM UTC
|
Trackback
|
 Sunday, August 31, 2008
Many times when I talk to a pro-dynamic typing person, they bring up duck typing. And when I say that duck typing could be resolved statically, I usually get wierd looks, or worse. Well F# exposes static duck typing to users. At least, using the definition that C# uses for duck typing of foreach and collection initalizers. (Yes, of course its a compiler feature and not true CLR/runtime checking.) I'm not promoting this, just pointing it out for fun.
F# allows inline values to accept "statically resolved type
variables". The F# specification says (§5.1.2):
A type of the form ^ident is a statically resolved variable type. A fresh type inference variable is created and added to the type inference environment (see §14.6). This type variable is tagged with an attribute indicating it may not be generalized except at inline definitions (see §14.7), and likewise any type variable with which it is equated via a type inference equation may similarly not be generalized.
At the end of this post I have a simple example to help understand this kind of type variable. But more interesting is a another constraint you can apply to such type variables. §5.1.5.3 Member Constraints: "A constraint of the form (typar or ... or typar) : (member-sig) is an explicit member constraint." But, inside the the F# library, this form is used with function application! For example, the char function is defined:
let inline char (x: ^a) = (^a : (static member ToChar: ^a -> char) (x)) // Function application! ...<snip /> -- I removed all the special case and inline IL code as its irrelevant for this post
Well, if we have member constraints with function application... we have "statically typed duck typing":
let inline speak (a: ^a) =
let x = (^a : (member speak: unit -> string) (a))
printfn "It said: %s" x
let y = (^a : (member talk: unit -> string) (a))
printfn "Then it said %s" y
type duck() =
member x.speak() = "quack"
member x.talk() = "quackity quack"
type dog() =
member x.speak() = "woof"
member x.talk() = "arrrr"
let x = new duck()
let y = new dog()
speak x
speak y
Outputs:
It said: quack Then it said quackity quack It said: woof Then it said arrrr
The restriction is that you have to use inline to get generalization*. If it's not inline, then it'll add additional constraints based on usage. If you removed inline in this case, you'd get the following:
warning FS0064: This construct causes code to be less generic than indicated by the type annotations. The type variable 'a has been constrained to be type 'duck'. error FS0001: The type 'dog' is not compatible with the type 'duck'.
Inline is as it sounds - the IL code is emitted inline, which is obviously a drawback in many cases. But that's the only way it can work - it has to statically know what types and compile the right method info into the binary. I suppose it'd be possible for the CLR to support this intrinsically. That way, the JIT could emit much more optimized code, versus creating a new method for reach type. I don't know dynamic languages well enough to know if this would be at all a help for interop.
*Here's a simple example to demonstrate the difference between 'a and ^a type parameters generalization.
> let id (a : 'a) = a;; val id : 'a -> 'a
> id 1, id "hi";; val it : int * string = (1, "hi") // Good, it's generic
> let id (a : ^a) = a;; let id (a : ^a) = a;; -------------^^ stdin(8,14): warning FS0064: This construct causes code to be less generic than indicated by the type annotations. The type variable 'a has been constrained to be type 'obj'.
val id : obj -> obj
> id 1, id "hi";; val it : obj * obj = (1, "hi") // Constrained to obj - not generic
> let inline id (a : ^a) = a;; val inline id : ^a -> ^a
> id 1, id "hi";; val it : int * string = (1, "hi") // Since it's inline, it's generic
|
|
FSharp
|
Sunday, August 31, 2008 1:14:34 AM UTC
|
Trackback
|
|
|