Thursday, May 17, 2012

Self Note: Seq and base class call

put the following code in a F# app. notice there is an error under let a. the error is "A protected member is called or 'base' is being used. This is only allowed in the direct implementation of members since they could escape their object scope." 

type BaseClass() =
    member val X = 0 with get, set

type ClassA() =
    inherit BaseClass()
    member this.F() =
        let r = seq {
            let a = 0
            let b = base.X
            yield a }

The fix is to change base to this. 

Saturday, May 12, 2012

Type Provider - Share Information by giving a customized base class

I have a "bad" habit. It is not easy for me to say forget about, let it go. Last night this little monster came back haunted me again. Mostly likely I won't be able to solve the original problem, instead I would be lured into a new territory. Last night the monster reminded me the unit test type provider and led me to think how to share the information between different type provider methods.

The current type provider's method call is isolated, there is few sample about how to maintain a internal state. The regex type provider is a sample. Once you have the matched group, that group is a self-sustained structure and do not need to interactive with other match group. What I am interested is how to make the information flow/share between type provider's method call. For example, I want Method2 to retrieve the information sent in Method1. The b should be "aa"
type T = MyTypeProvider
let t = T()
let b = t.Method2()
The approach I am going to use is to use the base class in the type provider. Current sample type providers always take obj as base class, while I want to use my class to store my own stuff. You can download the code from here.

The base class used to hold the information passed in method1 is X. Please make sure the type and member  must be public.

type BaseType2() =
     member val X = "" with get, set
The generated code take the first element %%args.[0] as "this" pointer and converted to the BaseType2. You do not have to convert to obj first and then to BaseType2. That conversion will generate an error when you use the type provider.
InvokeCode = fun args -> <@@ (%%args.[0]:BaseType2).X <- (%%args.[1]) @@>)
The important is the constructor, please return a BaseType2(). Otherwise, a runtime error complains about NULL reference.  I guess that's everything you needs to get it working.

the testing program is:

#r @".\bin\Debug\ShareInfoSampleTypeProvider.dll"

type T = Samples.ShareInfo.TPTest.TPTestType
let t = T()
let a = t.F2()

printfn "the parameter in F1 is %A" a

and result should be:

the parameter in F1 is "aa"

This technique open a door to extend the existing types in a more automated way. You can use the extension method to extend the type, but you have to type each character. Now you can write your type provider to extend your types. Your program will generate the methods and properties.

Allow me some time to think about a good sample, now it is time to catch up some sleep.. :)

Friday, May 11, 2012

F# on Cloud - 3 More Azure Samples

As leading the Shanghai F# team since Monday, I decide to enhance F#'s presence in the cloud computing. Actually more and more team inside and outside Microsoft use F# in their projects. And I have some information about other big names are using functional programming language as their major cloud programming language. Cannot really mentioned names for some well-known reason.. ;-)

I am excited that  I can be part of this process. Although other companies do not always use F#, but at least the trend is there. The Shanghai team has published three more F# samples. Because they are project (not a single file snippet), you have to download it before you can take a look. One picture is more than 1000 words, so take a look at the pictures below and you will know what you will get.

Hopefully you can learn something from these samples and get to love F# and cloud.. :-)

Please leave comments and requirement either here or on the web site. Please remember that the one of zip file is for Visual Studio 11 Preview. This is required for another several months because of our support policy.

Name restriction

Today I got a customer report about a wired error message generated when using reference cell.

the code is like:

module AA
let a = ref 0
let get_a() = !a

this will report the duplicated definition of a, actually it is not. For now the workaround is to rename the function to something difference. We are fixing it.

Wednesday, May 9, 2012

Type Provider query on client side

When running some sort of check on F# before the the next phase. I decided to record some difficulties during the process and hopefully make our F# user's life easier. You can ignore this if you have already what is going on.

the query below is perfectly fine, right? At first glance, it is. But the exception soon throw at your face, make you wondering what could be the problem.

let internal q1 = query {
    for n in db.Course do
    select (sprintf "course name = %s" n.CourseName) }

let result1 = showResult q1

It is the because the sprintf can not be translated to the server side statements; however, the cause does not help you solve the problem. The fix is to move the computation to client side.

let internal q1 = query {
    for n in db.Course.AsEnumerable() do
    select (sprintf "course name = %s" n.CourseName) }

Hopefully this can save you several F words .. :-D 

Debug Async F# try..catch statement

Look at the following code. When you try to debug it, the behavior seems odd when you see a dialog popup telling you the exception is not handled by user code. You might just wondering I put the :? Exception there and even get a warning say all the exception is handled. The trick is to go to Tools->Options->Debug settings and turn off "Just my code".

The dialog says unhandled by user code, the exception is handled by F# library code which is not user code.

module File1

open System

let asyncOperationWithExcpetion =
    async {
            failwith "error"
            | :? ArgumentException as e -> printfn "exception... :( "
            | :? Exception as e -> printfn "handled anyway"

asyncOperationWithExcpetion |> Async.Start


printfn "aa"  //put break point here!

F# Choice

Today I was puzzled by the Choice type. I thought it was two values in a structure. Actually it was one union-like structure and only store one value. I did not want to stop on my mistake and want to explore more. And I find the Choice is choice-sensitive even their type is same. Unlike C++, the value share the same memory storage.

See the below code:

open System

let a : Choice< int, int > = Choice2Of2 1

let f = function
        | Choice1Of2 e -> printfn "1 %A" e
        | Choice2Of2 e -> printfn "2 %A" e

f a

it outputs 2 1 because it goes to the highlighted route.

Friday, May 4, 2012

Self Note: printfn output interleaving

Interesting enough, when doing the printfn in a multi-threading environment, you will see the output interleaving. This does not mean the printfn is not thread-safe. To comprise this, you need to use lock.

let syncRoot = ref 0
lock(syncRoot) ( fun _ ->  printfn "not interleaving printfn" )

Thursday, May 3, 2012

NULL value in F# and C#

First of all, NULL is not allowed value for F# type. This is good in some sense and consequently brings trouble.

The first requirement might be: forget about all the argument about option and NULL, I need NULL, that's the way I program. Fine. You can use [< AllowNullLiteral >] attribute on the type you allow it to be NULL.

Some people really into F# will agree about using option is a better choice. However, this restriction makes F# program difficult to communicate with C# program. For example, you want to use F# library from C#. Can you really forbidden C# user set NULL value into your perfect world? Or Should you? Instead of being a coding police and forcing everyone, I would like to keep my world clean and let others to continue their way. I will use a property to filter the incoming NULL to a option value, expose NULL value to outside world, and, in the meanwhile, protected my inside world from being contaminated by NULL.

The following code use the Unchecked.default function. Unfortunately, the match won't work very well in the setter function, so I just use if...else.

type A() =
    let mutable a:option< A > = None
    member this.Value
        with get() =
            match a with
            | Some(n) -> n
            | None -> Unchecked.defaultof< A >
        and set(v) =
            if v = Unchecked.defaultof< A > then a <- None
            else a <- Some(v)

In this way, I can keep NULL from my code and still provide a nice C# experience to C# users.

if you really need to check the NULL value in the F# code, you can always use box(yourValue) and then it is OK to do box(yourValue) = null.

 Please remove some space if the code does not compile, the blog editor does not work very well with F# code.. :(

Tuesday, May 1, 2012

Setup Azure Database user permission

When doing the F# Azure stuff, I bump into issues about setting up a Azure database which supposed to be easy, but not!

When I am asked why I want to do this, I really got upset. Just answer my question and stop asking me why! Should I tell you I do not want to share my password? Must be a "won't fix" or "by design" scenario where I should only create one user. Ok, Ok, all my fault.

Anyway, here is the procedure:

I use Visual Studio 11's Server Explorer and then create a "new query" from right-click menu.

  • Create login in the master database
    CREATE LOGIN login0 WITH password='< your password >';
  • create user in the database you want to login, remember to switch out of master database
    CREATE USER user0 FROM LOGIN login0;

  • grant permission to the user

the last step is to set up the permission by choose one of the following

EXEC sp_addrolemember 'db_datareader''user0'
EXEC sp_addrolemember 'db_datawriter'' user0 '
EXEC sp_addrolemember 'db_accessadmin'' user0 '
EXEC sp_addrolemember 'db_backupoperator'' user0 '
EXEC sp_addrolemember 'db_ddladmin'' user0 '
EXEC sp_addrolemember 'db_denydatareader'' user0 '
EXEC sp_addrolemember 'db_denydatawriter'' user0 '
EXEC sp_addrolemember 'db_owner'' user0 '
EXEC sp_addrolemember 'db_securityadmin'' user0 '
remember to use login0 to login.

GRANT EXECUTE ON < stored procedure >  TO user0
deny select on < table or view > to user0