Hi,
it can be done, but there are restrictions. This is really not something that could be represented by the .NET at runtime, which means that the compiler has to process this only during compilation (just like C++ templates). However, you can write this:
1 2
let inline parse< ^a when ^a : (static member Parse : string -> ^a) > (s:string) : ^a = (^a : (static member Parse : string -> ^a) (s))
The keyword "inline" is required, so that the compiler knows that the function should be inlined and the "^a" type parameter is instantiated.
T.
Hi,
the compiler has to process this only during compilation (just like C++ templates).
Perfect! I'm a C++ programmer at heart, and having used C# Generics a bit I've always felt terribly limited by this aspect of C# generics. This makes me love F# even more. I have a question though. Let's say now that this member is not static, but instead requires an instance. A good example is the StreamWriter.Write() method. Ideally I would like to be able to write code such as:
1 2 3 4
let writer = new StreamWriter(new FileStream("test.txt", IO.FileMode.Create)) Write2 writer 4 Write2 writer "test" Write2 writer 'a'
Of course I would like this code to be able to work not only with StreamWriters, but with any class that has a method called Write that takes one argument. I have tried something like the following:
1 2
let inline Write2< ^a when ^a : (member Write : 'b -> unit) > (w:^a) (s:'b) : unit = (w : (member Write : 'b -> unit) (s))
but this does not work. I have to wonder though. This seems really complex when in theory it seems like it should be easy (like it is in C++). Even in the first example (the static case), is there some technical compiler limitation preventing me from writing the much more obvious syntax
1 2
let inline parse< ^a when ^a : (static member Parse : string -> ^a) > (s:string) : ^a = (^a.Parse(s))
and in the second case
1 2 3 4
let inline Write2< ^a when ^a : (member Write : 'b -> unit) > (w:^a) (s:'b) : unit = (w.Write(s))
Of course I would like this code to be able to work not only with StreamWriters, but with any class that has a method called Write that takes one argument. I have tried something like the following:
1 2let inline Write2< ^a when ^a : (member Write : 'b -> unit) > (w:^a) (s:'b) : unit = (w : (member Write : 'b -> unit) (s))but this does not work.
Have you seen this page? It shows how to do what you want for a non-static member taking no argument. I have made multiple attempts to guess what the syntax would be for a non-static member taking arguments, but without success so far.
I have also managed to crash the compiler (bug reported and registered) when playing with constraints, and I am therefore staying clear of them for the moment.
I find the alternative shown by tomasp to be more elegant, and more general as well. What if the users of your generic code are not using classes, or what if their write methods are called something else, e.g. writeToFile?
Have you seen this page? It shows how to do what you want for a non-static member taking no argument. I have made multiple attempts to guess what the syntax would be for a non-static member taking arguments, but without success so far.
Ok, I got it to work with one argument:
1 2 3 4 5 6 7 8 9 10 11 12
type duck() = member x.speak() = "hi!" member x.talk() = "hello!" member x.repeat(s:string) = s 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 let z = (^a : (member repeat: string -> string) (a, "whassup")) printfn "Then it repeated %s" z
Thanks for the link, hadn't seen it!
Edit: I still can't get it to work if the methods are overloaded on signature though. E.g. there is another member x.repeat(n:int) = n. Whichever call comes first is the one that is used throughout the rest of the function. e.g. if you try to call it with an int first, calling it with a string later is invalid, even if you use the correct constraint syntax. Oh well, not a big deal, just a curiosity.
What if the users of your generic code are not using classes, or what if their write methods are called something else, e.g. writeToFile?
Well, it would need to be restricted to code internal to a particular library, and not accessible to anyone but me. Perhaps part of the problem is that I'm still "thinking in C++". In C++ pretty much all of STL and boost are built on this concept using templates, which is the reason that they are as powerful as they are. Of course you don't -need- any of these concepts to achieve genericity in F# or any functional language, since obviously polymorphic compile time construct can easily be converted into an equivalent runtime polymorphic construct. Like tomasp mentioned in a previous post, we can just pass a lambda function to make the write call. But still, I can't help but think the technique might eventually be useful, even if only as a micro-optimization to remove one function call.
Plus I always try to learn the esoteric and difficult features of a language first :)
What if the users of your generic code are not using classes, or what if their write methods are called something else, e.g. writeToFile?
Well, it would need to be restricted to code internal to a particular library, and not accessible to anyone but me. Perhaps part of the problem is that I'm still "thinking in C++". In C++ pretty much all of STL and boost are built on this concept using templates, which is the reason that they are as powerful as they are. Of course you don't -need- any of these concepts to achieve genericity in F# or any functional language, since obviously polymorphic compile time construct can easily be converted into an equivalent runtime polymorphic construct. Like tomasp mentioned in a previous post, we can just pass a lambda function to make the write call. But still, I can't help but think the technique might eventually be useful, even if only as a micro-optimization to remove one function call.
Plus I always try to learn the esoteric and difficult features of a language first :)
Actually, I have been there :) I'm also coming from the C++ world, and templates have always fascinated me. One thing you might have missed is that all it takes to have compile-time polymorphism in F# (with its speed benefits and its code size blowup disadvantages) is to declare generic functions as inline.
I hope I am not saying anything incorrect if I say that in F#, the "inline" keyword differs from C and C++. In these languages, "inline" is just a hint that compilers may ignore. In F#, "inline" function definitions will always cause the compiler to generate code at call sites. See what happens when you define a recursive inline function (you get an error). Disclaimer: That information is based on my personal experiments, I haven't found in the F# manual what "inline" exactly means.
What I'd really like is some macro system in F#. Something that would give us an AST, then allow us to return an AST, at compile time.
I've not used member constraints _that_ often. But, when I do, they're always very useful, and allow me to refactor code I otherwise would not be able to. (Say, I have 10 different object sequences, and I want to pull out the name in each one. It just so happens that all of them have the name "Name", so an inline expansion is much nicer than having to provide a textually-equivalent lambda (fun x -> x.Name) )
Anyways, I'd think a macro system would be even more useful. Also, it'd probably be difficult enough that perhaps it would not be abused like textual macros are.
Ah, sorry for the confusion.
It really works even for non-static members. I thought that's not possible! (but still, I'd say that this is a feature that should be used carefuly, but I can see many useful applications...)
T.
Hi,
I think this is more a philosphical question. Technically, I guess it could be possible to allow this, howeve this feature is quite limited in F# (you have to use inlining) and is really intended mostly for numerical code where you want to use same functions for various numeric types.
I think that F# usually has different ways for achieving things like this. Your "write2" can for example take a writing function as an argument:
1 2 3 4 5 6 7
> let write2 (f:'a -> unit) (v:'a) = f v;; val write2 : ('a -> unit) -> 'a -> unit > "Hello" |> write2 writer.WriteLine;; // Note, if we write "Hello" on the left using pipelining, // the F# compiler will be able to resolve the overloaded "WriteLine" method val it : unit = ()
BTW: I used C++/CLI a bit and I thought that the combination of generics and templates is really powerful, however it is really mind-blowing, so I guess it is better to keep the language (relatively) simple, but I agree that this is a limitation - I'm just not sure whether templates like those in C++ would make F# code better... At least there is some area where the F# team can do improvements once the version first version is officially relesed :-)!
Topic tags
- f# × 3705
- websharper × 1897
- compiler × 286
- functional × 201
- ui next × 139
- c# × 121
- classes × 97
- web × 97
- .net × 84
- book × 84
- async × 76
- ui.next × 67
- bug × 54
- core × 49
- website × 49
- server × 45
- parallel × 43
- ui × 43
- enhancement × 41
- parsing × 41
- testing × 41
- trywebsharper × 41
- typescript × 37
- html × 35
- javascript × 35
- owin × 35
- asynchronous × 30
- monad × 28
- ocaml × 28
- tutorial × 27
- warp × 27
- haskell × 26
- sitelet × 25
- linq × 22
- workflows × 22
- wpf × 20
- fpish × 19
- introduction × 19
- silverlight × 19
- sitelets × 19
- monodevelop × 17
- rpc × 17
- suave × 17
- piglets × 16
- collections × 15
- feature request × 15
- jquery × 15
- templates × 15
- getting started × 14
- pipeline × 14
- kendoui × 13
- reactive × 12
- 4.1.0.171 × 11
- monads × 11
- opinion × 10
- 4.0.190.100-rc × 9
- deployment × 9
- fixed × 9
- formlets × 9
- in × 9
- json × 9
- plugin × 9
- proposal × 9
- scheme × 9
- solid × 9
- basics × 8
- concurrent × 8
- highcharts × 8
- how-to × 8
- python × 8
- 4.1.1.175 × 7
- complexity × 7
- documentation × 7
- visual studio × 7
- 4.1.2.178 × 6
- lisp × 6
- real-world × 6
- released in 4.0.192.103-rc × 6
- remoting × 6
- resources × 6
- scala × 6
- websharper ui.next × 6
- workshop × 6
- xaml × 6
- 4.0.193.110 × 5
- 4.2.3.236 × 5
- aspnetmvc × 5
- authentication × 5
- azure × 5
- bootstrap × 5
- conference × 5
- dsl × 5
- formlet × 5
- java × 5
- list × 5
- metaprogramming × 5
- ml × 5
- released in Zafir.4.0.188.91-beta10 × 5
- sql × 5
- visualstudio × 5
- websharper.forms × 5
- zafir × 5
- 4.0.192.106 × 4
- 4.0.195.127 × 4
- 4.1.0.38 × 4
- 4.2.1.86 × 4
- 4.2.6.118 × 4
- css × 4
- example × 4
- extensions × 4
- fsi × 4
- fsx × 4
- html5 × 4
- jqueryui × 4
- lift × 4
- reflection × 4
- remote × 4
- rest × 4
- spa × 4
- teaching × 4
- template × 4
- websocket × 4
- wontfix × 4
- 4.0.196.147 × 3
- 4.1.0.34 × 3
- 4.1.6.207 × 3
- 4.2.1.223-beta × 3
- 4.2.11.258 × 3
- 4.2.4.114 × 3
- 4.2.4.247 × 3
- 4.2.5.115 × 3
- 4.2.6.253 × 3
- 4.2.9.256 × 3
- ajax × 3
- alt.net × 3
- aml × 3
- asp.net mvc × 3
- canvas × 3
- cloudsharper × 3
- compilation × 3
- database × 3
- erlang × 3
- events × 3
- extension × 3
- file upload × 3
- forums × 3
- inline × 3
- issue × 3
- kendo × 3
- macro × 3
- mono × 3
- msbuild × 3
- mvc × 3
- pattern × 3
- piglet × 3
- released in Zafir.4.0.187.90-beta10 × 3
- svg × 3
- type provider × 3
- view × 3
- 4.1.1.64 × 2
- 4.1.5.203 × 2
- 4.1.7.232 × 2
- 4.2.10.257 × 2
- 4.2.3.111 × 2
- 4.2.5.249 × 2
- android × 2
- asp.net × 2
- beginner × 2
- blog × 2
- chart × 2
- client × 2
- client server app × 2
- clojure × 2
- computation expressions × 2
- constructor × 2
- corporate × 2
- courses × 2
- cufp × 2
- d3 × 2
- debugging × 2
- direct × 2
- discriminated union × 2
- docs × 2
- elm × 2
- endpoint × 2
- endpoints × 2
- enterprise × 2
- entity framework × 2
- event × 2
- f# interactive × 2
- fable × 2
- flowlet × 2
- formdata × 2
- forms × 2
- fsc × 2
- google maps × 2
- hosting × 2
- http × 2
- https × 2
- iis 8.0 × 2
- install × 2
- interactive × 2
- interface × 2
- iphone × 2
- iteratee × 2
- jobs × 2
- jquery mobile × 2
- keynote × 2
- lens × 2
- lenses × 2
- linux × 2
- listmodel × 2
- mac × 2
- numeric × 2
- oauth × 2
- obfuscation × 2
- offline × 2
- oop × 2
- osx × 2
- packaging × 2
- pattern matching × 2
- performance × 2
- pipelines × 2
- q&a × 2
- quotation × 2
- reference × 2
- released in Zafir.4.0.185.88-beta10 × 2
- rx × 2
- script × 2
- security × 2
- self host × 2
- seq × 2
- sockets × 2
- stm × 2
- tcp × 2
- trie × 2
- tutorials × 2
- type × 2
- url × 2
- var × 2
- websharper.charting × 2
- websharper4 × 2
- websockets × 2
- wig × 2
- xna × 2
- zh × 2
- .net interop × 1
- 2012 × 1
- 4.0.194.126 × 1
- 4.1.3.184 × 1
- 4.1.4.189 × 1
- 4.2.0.214-beta × 1
- 4.2.12.259 × 1
- 4.2.2.231-beta × 1
- 4.2.8.255 × 1
- Canvas Sample Example × 1
- DynamicStyle Animated Style × 1
- Fixed in 4.0.190.100-rc × 1
- Released in Zafir.UI.Next.4.0.169.79-beta10 × 1
- SvgDynamicAttribute × 1
- WebComponent × 1
- abstract class × 1
- accumulator × 1
- active pattern × 1
- actor × 1
- addin × 1
- agents × 1
- aggregation × 1
- agile × 1
- alter session × 1
- animation × 1
- anonymous object × 1
- apache × 1
- api × 1
- appcelerator × 1
- architecture × 1
- array × 1
- arrays × 1
- asp.net 4.5 × 1
- asp.net core × 1
- asp.net integration × 1
- asp.net mvc 4 × 1
- asp.net web api × 1
- aspnet × 1
- ast × 1
- attributes × 1
- authorization × 1
- b-tree × 1
- back button × 1
- badimageformatexception × 1
- bash script × 1
- batching × 1
- binding-vars × 1
- bistro × 1
- body × 1
- bundle × 1
- camtasia studio × 1
- cas protocol × 1
- charts × 1
- clarity × 1
- class × 1
- cli × 1
- clipboard × 1
- clojurescript × 1
- closures × 1
- cloud × 1
- cms × 1
- coding diacritics × 1
- color highlighting × 1
- color zones × 1
- combinator × 1
- combinators × 1
- compile × 1
- compile code on server × 1
- config × 1
- confirm × 1
- content × 1
- context × 1
- context.usersession × 1
- continuation-passing style × 1
- coords × 1
- cordova × 1
- cors × 1
- coursera × 1
- cross-domain × 1
- csla × 1
- current_schema × 1
- custom content × 1
- data × 1
- data grid × 1
- datetime × 1
- debug × 1
- declarative × 1
- delete × 1
- devexpress × 1
- dhtmlx × 1
- dictionary × 1
- directattribute × 1
- disqus × 1
- distance × 1
- do binding × 1
- doc elt ui.next upgrade × 1
- docker × 1
- dojo × 1
- dol × 1
- dom × 1
- domain × 1
- du × 1
- duf-101 × 1
- dynamic × 1
- eastern language × 1
- eclipse × 1
- edsl × 1
- em algorithm × 1
- emacs × 1
- emotion × 1
- enums × 1
- error × 1
- etw × 1
- euclidean × 1
- eventhandlerlist × 1
- examples × 1
- ext js × 1
- extension methods × 1
- extra × 1
- facet pattern × 1
- failed to translate × 1
- fake × 1
- fantomas × 1
- fear × 1
- float × 1
- form × 1
- form-data × 1
- forum × 1
- fp × 1
- frank × 1
- fsdoc × 1
- fsharp × 1
- fsharp.core × 1
- fsharp.powerpack × 1
- fsharpx × 1
- fsunit × 1
- function × 1
- functional style × 1
- game × 1
- games × 1
- gc × 1
- generic × 1
- geometry × 1
- getlastwin32error × 1
- getting-started × 1
- google × 1
- google.maps × 1
- grid × 1
- group × 1
- guide × 1
- hash × 1
- headers × 1
- hello world example × 1
- heroku × 1
- highchart × 1
- history × 1
- how to × 1
- html-templating × 1
- http405 × 1
- httpcontext × 1
- hubfs × 1
- i18n × 1
- ie 8 × 1
- if-doc × 1
- iis × 1
- image × 1
- images × 1
- inheritance × 1
- initialize × 1
- input × 1
- install "visual studio" × 1
- installer × 1
- int64 × 1
- interfaces × 1
- internet explorer × 1
- interop × 1
- interpreter × 1
- io × 1
- iobservable × 1
- ios × 1
- iot × 1
- ipad × 1
- isomorphic × 1
- javascript optimization × 1
- javascript semanticui resources × 1
- jquery-plugin × 1
- jquery-ui × 1
- jquery-ui-datepicker × 1
- js × 1
- kendo datasource × 1
- kendochart × 1
- kendoui compiler × 1
- knockout × 1
- l10n × 1
- learning × 1
- library × 1
- libs × 1
- license × 1
- licensing × 1
- lineserieszonescfg × 1
- local setting × 1
- localization × 1
- logging × 1
- loop × 1
- macros × 1
- mailboxprocessor × 1
- mapping × 1
- maps × 1
- markerclusterer × 1
- markup × 1
- marshal × 1
- math × 1
- mathjax × 1
- message × 1
- message passing × 1
- message-passing × 1
- meta × 1
- metro style × 1
- micro orm × 1
- minimum-requirements × 1
- mix × 1
- mobile installation × 1
- mod_mono × 1
- modal × 1
- module × 1
- mouseevent × 1
- mouseposition × 1
- multidimensional × 1
- multiline × 1
- multithreading × 1
- mysql × 1
- mysqlclient × 1
- nancy × 1
- native × 1
- nested × 1
- nested loops × 1
- node × 1
- nunit × 1
- object relation mapper × 1
- object-oriented × 1
- om × 1
- onboarding × 1
- onclick × 1
- optimization × 1
- option × 1
- orm × 1
- os x × 1
- output-path × 1
- override × 1
- paper × 1
- parameter × 1
- persistence × 1
- persistent data structure × 1
- phonegap × 1
- pola × 1
- post × 1
- powerpack × 1
- prefix tree × 1
- principle of least authority × 1
- privacy × 1
- private × 1
- profile × 1
- programming × 1
- project × 1
- project euler × 1
- projekt_feladat × 1
- protected × 1
- provider × 1
- proxy × 1
- ptvs × 1
- public × 1
- pure f# × 1
- purescript × 1
- qna × 1
- quant × 1
- query sitelet × 1
- question × 1
- quotations × 1
- range × 1
- raphael × 1
- razor × 1
- rc × 1
- reactjs × 1
- real-time × 1
- ref × 1
- region × 1
- released in 4.0.190.100-rc × 1
- reporting × 1
- responsive design × 1
- rest api × 1
- rest sitelet × 1
- restful × 1
- round table × 1
- router × 1
- routing × 1
- rpc reverseproxy × 1
- runtime × 1
- sales × 1
- sample × 1
- sampleapp × 1
- scriptcs × 1
- scripting × 1
- search × 1
- self hosted × 1
- semanticui × 1
- sequence × 1
- serialisation × 1
- service × 1
- session-state × 1
- sharepoint × 1
- signals × 1
- sitelet website × 1
- sitelet.protect × 1
- sitlets × 1
- slickgrid × 1
- source code × 1
- sqlentityconnection × 1
- ssl × 1
- standards × 1
- static content × 1
- stickynotes × 1
- streamreader × 1
- stress × 1
- strong name × 1
- structures × 1
- submitbutton × 1
- subscribe × 1
- svg example html5 websharper.ui.next × 1
- sweetalert × 1
- system.datetime × 1
- system.reflection.targetinvocationexception × 1
- table storage × 1
- targets × 1
- tdd × 1
- templates ui.next × 1
- templating × 1
- text parsing × 1
- three.js × 1
- time travel × 1
- tls × 1
- tooltip × 1
- tracing × 1
- tsunamiide × 1
- turkish × 1
- twitter-bootstrap × 1
- type erasure × 1
- type inference × 1
- type providers × 1
- type-providers × 1
- typeprovider × 1
- ui next forms × 1
- ui-next × 1
- ui.next jqueryui × 1
- ui.next charting × 1
- ui.next formlets × 1
- ui.next forms × 1
- ui.next suave visualstudio × 1
- ui.next templating × 1
- unicode × 1
- unittest client × 1
- upload × 1
- usersession × 1
- validation × 1
- vb × 1
- vb.net × 1
- vector × 1
- view.map × 1
- visal studio × 1
- visual f# × 1
- visual studio 11 × 1
- visual studio 2012 × 1
- visual studio shell × 1
- vs2017 compiler zafir × 1
- vsix × 1
- web api × 1
- web-scraping × 1
- webapi × 1
- webcomponents × 1
- webforms × 1
- webgl × 1
- webrtc × 1
- webshaper × 1
- websharper async × 1
- websharper codemirror × 1
- websharper f# google × 1
- websharper forms × 1
- websharper reactive × 1
- websharper rpc × 1
- websharper sitelets routing × 1
- websharper warp × 1
- websharper-interface-generator × 1
- websharper.chartsjs × 1
- websharper.com × 1
- websharper.exe × 1
- websharper.owin × 1
- websharper.ui.next × 1
- websharper.ui.next jquery × 1
- websockets iis × 1
- why-websharper × 1
- windows 7 × 1
- windows 8 × 1
- windows-phone × 1
- winrt × 1
- www.grabbitmedia.com × 1
- xamarin × 1
- xml × 1
- yeoman × 1
- yield × 1
- zafir beta × 1
- zafir websharper4 × 1
- zarovizsga × 1
![]() |
Copyright (c) 2011-2012 IntelliFactory. All rights reserved. Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy |
Built with WebSharper |
Suppose I want to write a function f that can take any type 'a with a Parse method that has the signature of which is 'b -> 'a. The function f would have the type 'b -> 'a as well. What would be the correct syntax to do this. So for example, I want a function called Parse2 such that the following works:
let x:int = Parse2 "7"
let y:int64 = Parse2 "4328032"
let z:DateTime = Parse2 "4/31/1998"
But the following does not:
let w:string = Parse2 "fail"
The implementation of Parse2 should look basically like this:
let fun Parse2 x =
'a.Parse(x)
Is this possible?