Without the numeric types implementing a common interface, typeclass support, automatic interface support, or anything else like that, I'm afraid it is not very pretty. (At least, that I know of, and especially if you want full type checking.)
Here is a generic version:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
let inline factor (n:^a) fail_if (one:^a) spf = let rec factor n factorization = match n with | _ when n = (one - one) || n = one -> factorization | _ -> let p = spf n match factorization with | (x,mult)::tl when (x=p) -> factor (n/p) ((x,mult + one)::tl) | prev::_ when (fail_if prev) -> [] | l -> factor (n/p) ((p,one)::l) factor n [] |> List.rev
Which produces the lovely type signature:
1 2 3 4 5 6 7 8 9 10
val inline factor : ^a -> ( ^c * ^a -> bool) -> ^a -> ( ^a -> ^c) -> ( ^c * ^a) list when ^a : (static member ( - ) : ^a * ^a -> ^a) and ^a : (static member ( + ) : ^a * ^a -> ^a) and ( ^a or ^c) : (static member ( / ) : ^a * ^c -> ^a)
(The BCL number types don't appear to have static members for Zero or One.)
Alternatively, you could create a type to store the functions you need (add, div, zero, one) and add that to your function. Something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
type 'a NumOps = { Zero : 'a One : 'a Add : 'a -> 'a -> 'a Div : 'a -> 'a -> 'a } let intops = { Zero = 0; One = 1; Add = (+); Div = (/); } let int64ops = { Zero = 0L; One = 1L; Add = (+); Div = (/); } let spf n ops = n let factor n fail_if ops = let rec factor n factorization = match n with | _ when n = (ops.Zero) || n = ops.One -> factorization | _ -> let p = spf n ops match factorization with | (x,mult)::tl when (x=p) -> factor (ops.Div n p) ((x,ops.Add mult ops.One)::tl) | prev::_ when (fail_if prev) -> [] | l -> factor (ops.Div n p) ((p,ops.One)::l) factor n [] |> List.rev
You could hide the ugly implementation and write a nice wrapper on top.
Now, if type extensions could figure into member constraints...
Actually, the Microsoft.FSharp.Core.LanguagePrimitives module has built-in members GenericZero and GenericOne which work on both primitive types as well as any other types with get_Zero and get_One static methods, respectively. Thus, the following code ought to work:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
open Microsoft.FSharp.Core.LanguagePrimitives let inline factor n fail_if smallest_prime_factor= let one = GenericOne let zero = GenericZero let rec factor n factorization = match n with | _ when n = zero || n = one -> factorization | _ -> let p = smallest_prime_factor n match factorization with | (x,mult)::tl when (x=p) -> factor (n/p) ((x,mult+one)::tl) | prev::_ when (fail_if prev) -> [] | l -> factor (n/p) ((p,one)::l) factor n [] |> List.rev
That's awesome, thanks! I have to wonder though, if there's any technical reason why the compiler couldn't be smart enough to know that the only type specific operations you've used (1 and 0) have generic equivalents and to emit IL using the generic equivalents instead, even though technically your sourec code would still contain the numbers 1 and 0.
If the int literals 0 and 1 were automatically coerced to whatever numeric type, then you run into the whole mess of numeric auto coercion and so on. For instance, 2.5 / 2 -- is that integer or floating division? (Right now it's a compiler error.)
It's definately inconvenient at times, but I feel that having implicit conversions ends up being more trouble than its worth. This is my personal experience dealing with C# and accidentally having things convert incorrectly. Perhaps there's a convenient-yet-unambiguous way to achieve this.
I tried to apply this to a prime number sequence coded and came up with the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
let rec smallest_prime_factor n = let limit = System.Math.Sqrt(float(n)) let rec smallest_prime_factor primes = match primes with | LazyList.Cons(p,_) when (GenericGreaterThan (float(p)) limit) -> n | LazyList.Cons(p,_) when (GenericEquality (n%p) GenericZero) -> p | LazyList.Cons(_,tail) -> smallest_prime_factor tail | _ -> failwith "Invalid prime sequence!" smallest_prime_factor primes and is_prime n = (GenericEquality (smallest_prime_factor n) n) and next_prime_after n = let GenericTwo = GenericOne + GenericOne let candidate = n+GenericTwo if (is_prime candidate) then candidate else next_prime_after candidate and all_primes_after n = let next = next_prime_after n LazyList.consf next (fun () -> all_primes_after next) and primes = let GenericTwo = GenericOne + GenericOne let GenericThree = GenericTwo + GenericOne LazyList.consf GenericTwo (fun () -> LazyList.consf GenericThree ( fun() -> all_primes_after GenericThree ) )
It still constraints all the types to be of type integer. Is it because of the modulus operator? If so, is there a way to fix it?
There are two issues: first of all, you need to mark your functions inline because of the type constraints that you need to express. Secondly, you'll get a warning that marking "primes" as 'inline' is deprecated because it's a value; this can be resolved by making it a function taking no arguments instead. With these two considerations in mind, here's what I get (edit: this won't actually work because "let rec inline" has some issues; you won't get any red squiggles in Visual Studio, but attempting to compile will give you an error):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
let rec inline smallest_prime_factor n = let limit = System.Math.Sqrt(float(n)) let rec smallest_prime_factor primes = match primes with | LazyList.Cons(p,_) when (GenericGreaterThan (float(p)) limit) -> n | LazyList.Cons(p,_) when (GenericEquality (n%p) GenericZero) -> p | LazyList.Cons(_,tail) -> smallest_prime_factor tail | _ -> failwith "Invalid prime sequence!" smallest_prime_factor (primes()) and inline is_prime n = (GenericEquality (smallest_prime_factor n) n) and inline next_prime_after n = let GenericTwo = GenericOne + GenericOne let candidate = n+GenericTwo if (is_prime candidate) then candidate else next_prime_after candidate and inline all_primes_after n = let next = next_prime_after n LazyList.consf next (fun () -> all_primes_after next) and inline primes() = let GenericTwo = GenericOne + GenericOne let GenericThree = GenericTwo + GenericOne LazyList.consf GenericTwo (fun () -> LazyList.consf GenericThree ( fun() -> all_primes_after GenericThree ) )
If the only function that other code needs to use is the "primes" function, it may make sense to refactor your code so that the primes is defined as a non-recursive inline function, with several local recursive functions (which then would not need to be marked inline). This would give you:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
let inline primes() = let rec smallest_prime_factor n = let limit = System.Math.Sqrt(float(n)) let rec smallest_prime_factor primes = match primes with | LazyList.Cons(p,_) when (GenericGreaterThan (float(p)) limit) -> n | LazyList.Cons(p,_) when (GenericEquality (n%p) GenericZero) -> p | LazyList.Cons(_,tail) -> smallest_prime_factor tail | _ -> failwith "Invalid prime sequence!" smallest_prime_factor primes and is_prime n = (GenericEquality (smallest_prime_factor n) n) and next_prime_after n = let GenericTwo = GenericOne + GenericOne let candidate = n+GenericTwo if (is_prime candidate) then candidate else next_prime_after candidate and all_primes_after n = let next = next_prime_after n LazyList.consf next (fun () -> all_primes_after next) and primes = let GenericTwo = GenericOne + GenericOne let GenericThree = GenericTwo + GenericOne LazyList.consf GenericTwo (fun () -> LazyList.consf GenericThree ( fun() -> all_primes_after GenericThree ) ) primes
This has the advantage that the inner definition of primes doesn't need to be converted into a function, but can remain a lazy list. (edit: It also has the advantage that it actually works!)
Well that certainly makes it easier. Thanks for the tip!
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 |
I've run into this problem numerous times now, but it seems really inconvenient writing generic functions to deal with numeric types. For example consider the following implementation of a function to factor integers:
Aside from the calling of function fail_if so that I can terminate the factorization early if some condition is met, this is a pretty boilerplate factorization method. But it works only for ints. It should work with int64s, or unsigned types, etc, but it seems I have to write another function to do that with basically the exact same code. Is there a method I'm missing to deal with this more generically?