Firma Dobrucki to przewozy autokarowe w graniach kraju i międzynarodowe do większości krajów w Europie. Przygotowaliśmy ofertę, która jest dostosowana do potrzeb wszystkich klientów, którzy cenią sobie wygodę i bezpieczeństwo. W taborze znajdują się markowe autobusy dostosowane do wyjazdów międzynarodowych oraz wygodne busy dla mniejszych grup. Nasza firma to nie tylko wynajem autokarów i wynajem busów. Specjalizujemy się dodatkowo w kompleksowej naprawie powypadkowych samochodów i autobusów. Dzięki długoletniemu doświadczeniu jesteśmy w stanie zreperować każdy zepsuty pojazd i przywrócić mu wygląd sprzed wypadku.
Hello. And Bye.HeyYYYYYYY
103
Sometimes I like a little something extra out of my online gaming. There has been a lot of buzz lately about websites where we can put some cash down on our online gaming for xbox or PS3. I have used several of these sites and thought I would share my experiences with them.
Virgin Gaming: [link:virgingaming.com]
Pros: Big cash prizes, automatic reporting
Cons: Noobish user base, inconsistent website makes it hard to find a match sometimes
Virgin is the biggest player with the most marketing dollars behind their site, but their inconsistent website is buggy and confusing. They have automatic reporting on all matches and a wide selection of games, but there seems to be many low rent players who use the service and its hard to find a good challenge.
Rivalspot: [link:www.rivalspot.com]
Pros: Weekly free tournaments with nice prizes, automatic reporting, great website
Cons: Still not a lot of users, less games
Rivalspot is a newcomer with a lot of potential, but they are still a bit young. They have weekly tournaments posted on their Facebook page where you can play for free and have a reasonable chance at winning a cash prize at the end. I like their blog, where they post a lot of great sports content, and there are a lot of great players who use the site - it is just still starting out so there aren't as many users online at any given time.
Gamersaloon: [link:www.gamersaloon.com]
Pros: Easy to find a game, good userbase, good game selection
Cons: No automatic reporting
Gamersaloon could be a strong player but to me the lack of automated reporting is a deal breaker. I just can't bring myself to trust some guy in some unknown part of the country being honest about the fact that I just pwned him.
Overall I always try Rivalspot first - they have the most potential IMHO, and when I can't find a match there I jump on to Virgin.
I know there are other similar sites out there, so I was hoping to make this into a thread where we can post about our experiences and maybe hook up for some cash gaming.
[link:kamagrapharma.blogspot.com]
Treten Sie mit Ihrem Doktor oder Apotheker für mehr Details in Verbindung.Schützen vor Feuchte und Tageslicht!Lagerung Lagern bei einer Zimmertemperatur zwischen 68-77 G (20 und 25 Grad Celsius).Vorsichtsmaßnahmen bevor Sie anfangen, Sildenafil zu nehmen, informieren Sie Ihren Doktor oder Apotheker, wenn Sie dazu allergisch sind; oder wenn Sie irgendwelche anderen Allergien haben.Alle Anweisungen für einen bestimmten Patienten sollen mit Ihrem Gesundheitspflegeberater oder Doktor, der für den Fall verantwortlich ist, übereingestimmt werden.Viele Menschen benutzen diese Medizin ohne ernste Nebenwirkungen.
Sildenafil beeinflusst die Reaktion zur sexuellen Anregung.Die ernste allergische Reaktion auf diese Medizin kann sehr selten auftreten und in diesem Fall brauchen Sie sofortige medizinische Hilfe.Verbrauch-Details Kamagra Mundgelee wird mündlich 15 bis 20 Minuten vor jeder möglicher sexuellen Aktivität verbraucht.Kamagra Mundgelee wird direkt in den Blutstrom aufgesogen und folglich erfordert es unterere Dosen, ähnliche Ergebnisse als das jeder möglicher anderen Medizin zu erzielen.Wir sind nicht für direkte, indirekte, spezielle oder andere indirekte Beschädigung irgendwelcher Art, sowie auch für Folgen Ihrer möglichen Selbstbehandlung nicht verantwortlich.Eine fettreiche Mahlzeit kann die Zeit des Effektes dieser Medizin verzögern.Wir müssen jede Haftung für Schäden ablehnen, die aus einer missbräuchlichen Nutzung von den Informationen auf dieser Seite entstehen kann.Dosierung und Anwendungsgebiet: Viagra Oral Jelly muss bei der Notwendigkeit genommen werden, ungefähr 1 Stunde vor dem Geschlechtsverkehr.Sildenafil kann für andere Zwecke auch verwendet werden, die nicht oben verzeichnet wurden.Bewahren Sie die Medizin in dem Badezimmer nicht.Haftung Wir stellen Ihnen nur die allgemeine Information über die Medizin zur Verfügung vor, die nicht alle Anweisungen, möglichen Wechselwirkungen oder Vorsichtmaßnahmen umfasst.Kamagra Mundgelee enthält Sildenafil Zitrat und wird in der Behandlung der aufrichtbaren Funktionsstörung in den Männern verwendet.Weg von Kindern und Haustieren halten.Verpasste Dosis Sie müssen diese Medizin nur bei der Notwendigkeit einnehmen, und brauchen keinen Plan für die Einnahme.Außer Reichweite von Kindern und Haustiere!Mögliche Nebenwirkung die möglichst oft auftretenden Nebenwirkungen sind Kopfschmerzen, Flushing , Übelkeit oder Magenverstimmung, verstopfte oder laufende Nase , Gedankenlosigkeit, Diarrhöe.Lagerung Kamagra Munddes gelee-15 und 30 Grad c-(59 und 86 Grade Fahrenheit) bei Zimmertemperatur speichern.Produktbeschreibung Allgemeine Anwendung Sildenafil ist der Hauptbestandteil von Viagra Oral Jelly www.Wechselwirkung diese Medizin soll nicht mit den Nitraten und entspannenden Arzneimitteln verwendet werden, die ?Gealterte Leute können für die Nebenwirkungen der Medizin empfindlicher sein.Es sollte einen Tag nicht mehrmals genommen werden.Diese Aufzählung kann unvollständig sein.Versuchen Sie, Grapefruit nicht zu essen oder Grapefruitsaft nicht zu trinken, während Sie mit Sildenafil behandelt werden.Diese Entspannung erlaubt erhöhten Blutfluss in die bestimmten Bereiche von Penis, und das führt zur Aufrichtung.Die Information, die auf dieser Seite ausgestellt ist kann nicht für die Selbstbehandlung oder für die Eigendiagnostik verwendet werden.Treten Sie mit Ihrem Doktor oder mit dem Apotheker in Verbindung.Es vor direkter Belastung durch Licht, Hitze und Feuchtigkeit schützen.Amyl- oder Butylnitrit enthalten; sowie Alphablockierer Arzneimitteln; andere Medikationen gegen Impotenz; Bestimmte Medikamente gegen Bluthochdruck, etc.
Sie müssen mit Ihrem Doktor sofort in Verbindung treten oder mit einem emergency Gesundheitszentrum in Verbindung treten, wenn Sie eine Überdosis vermuten.Nehmen Sie Viagra Oral Jelly nicht öfter als einmal täglich.Symptome für die Überdosierung können wie folgt sein: Brustschmerzen, Übelkeit, unregelmäßiger Herzschlag, Ohnmacht.Wenn Sie denken, Sie haben zu viel Medizin eingenommen, treten sofort mit Ihrem Arzt in Verbindung!Sildenafil verwendet man für die Behandlung der Funktionsstörung (Machtlosigkeit) bei den Männern, sowie bei dem arteriellen Lungenbluthochdruck.Kontraindikationen Viagra Oral Jelly ist für jene Patienten nicht angezeigt, die eine andere Medizin gegen Impotenz einnehmen oder Nitrate haltige Arzneien gegen Brustschmerzen oder Herzprobleme verwenden.Das wirkt auf die Entspannung der glatten Muskeln mit der Benutzung von Stickstoffoxid, einer Chemikalie, die normalerweise die Reaktion für sexuelle Anregung befreit.Diese Medizin sollte nicht von den Frauen und von den Kindern genommen werden, sowie von den Patienten mit einer bekannten Überempfindlichkeit zu irgendeinem Bestandteil der Pille.
[link:kamagrapharma.blogspot.com]
[YOUTUBE=" -CBf8O1tEMo "]sie sucht ihn[/YOUTUBE]
[YOUTUBE] -CBf8O1tEMo[/YOUTUBE]
[YOUTUBE]MeUHkpVgFLk[/YOUTUBE]
[YOUTUBE]HAbLhVuqaC8[/YOUTUBE]
[YOUTUBE="MeUHkpVgFLk"]internet kasino[/YOUTUBE]
[YOUTUBE="HAbLhVuqaC8"]internet kasinoh[/YOUTUBE]
[YOUTUBE]aWIOBTUZ0cM [/YOUTUBE]
[YOUTUBE]yK9zGb5WPn8[/YOUTUBE]
[YOUTUBE="aWIOBTUZ0cM "]internet kasino[/YOUTUBE]
[YOUTUBE="yK9zGb5WPn8"]internet kasinoh[/YOUTUBE]
eskrima.co
I've responded to DeeJay's excellent comments in the addendum to the article above.
This is fanastic,
1 2 3 4 5 6 7 8 9 10
type mypoint = {x:int ; y:int} with interface System.IComparable with member p1.CompareTo(p2:obj) = compare p1.y (p2 :?> mypoint).y end end;; compare {x=3; y=2} {x=2; y=3};; => -1 {x=3; y=2} < {x=2; y=3};; => true
without the custom implementation of IComparable... those results would be +1 and false
So because of this... is there any reason at all to be able to pass in custom comparators?
If there is any gain, is it worth the hassle of more complex implementation over just declaring your comparator when you declare your type?
And in the case where you may want to change the comparision of an existing type... can you not just wrap the type? or inherit from it...
although before I get too carried away here's an example i'm not sure quite how to solve yet.
if i want to do a custom comparator for int's. Say compare them when they are rounded down to the nearest 10...
i can't do
type myint = int
with
interface ....
as you can't augment a type abbreviation
nor can i do
1 2 3 4
type myint = class inherit int .... as int (Int32) is a sealed type
so maybe in this case i really only can wrap the type or delegate to it in a class.
Comparing to Haskell with it's type classes... it doesn't seem like you'd be much better off in this case, as you can't declare instances of a type class with type abbreviations so you'd have to wrap it in a newtype declaration.
Comparing to SML and OCaml with it's functors... this is where the functors win big. Parameterising code (structures) seems to be an incredibly powerful tool for writing datastructures.
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 |
DeeJay has just written an article showing how to implement a binomial heap using F#, adopting a purely functional style. One of his questions concerns structural comparison in F#. In this article I'll give a brief description of what structural comparison is and how you can define the meaning of structural comparison on your new type definitions.
Firstly, every time you're using operators such as <, >, <=, >=, =, <>, 'compare', 'min' and 'max' in F# code you may be invoking structural comparison. You may also be using structural comparison and structural hashing when you use the default configurations of F# data structures such as Set, Map, HashSet and HashTable (see also Hashtbl module) or when you instantiate .NET data structures using the values produced by Microsoft.FSharp.Collections.HashIdentity.Structural (new in F# 1.1.11.4).
On ordinary simple types like integers and strings structural comparison does exactly what you expect. It also works just as you'd expect for .NET types that implement the IComparable interface, e.g. System.DateTime values. However, they great thng is that you can also use these operators on structured types. For example, you can use them on F# tuple values, where a lexicographic left-to-right comparison is used:
As you can see, the purpose of structural comparison is to compositionally extend the orderings on primitive nodes to give a total ordering to concrete tree or DAG structured data. For example, you can also use structural comparison with list and array values:
The F# informal language specification has a section on structural comparison and hashing. In F# 1.1.10 the definition of structural comparison on some other types such as arrays is built into the F# compiler. In the soon-to-be-releaed F# 1.1.11.x these will be more transparent as this code will be implemented in F# code in the F# library fslib.dll itself. It's important to note tht structural comparison is not implemented using reflection - instead code is auto-generated for each type definition that implements comparison efficiently and where possible uses "fast path" comparison techniques, e.g. the generated code will use primitive IL/native instructions for integer comparisons. This means that in practice structural comparison is typically very very fast when used with appropriately sized keys.
By default, every new F# type definition is given an implementation of structural comparison. DeeJay's particular question is whether the semantics of structural comparison can be modified for new type definitions. The answer is yes! It's done simply by implementing the interface Systemm.IComparable. Here's an example, taken from the F# library. Firstly, the type definition includes a promise that the type will implement the the two relevant interfaces:
If you like you can define the implementations of these interfaces immediately at the point of the type definition. However you can also use a module of F# code that implements the functionality using a value-oriented style, which can be very useful for controlling the complexity of an implementation. Here's a fragment of the BigInt implementation, defining lessThan in terms of operations defined for BigNat:
You then later in the file augment the type definition with the implementations of these interfaces:
Some notes:
Some general additional notes on structural comparison and hashing:
Please feel free to ask for clarifications on any points regarding the specification of structural comparison and hashing
Cheers!
Don
Addendum:
DeeJay asks some great questions:
So because of this... is there any reason at all to be able to pass in custom comparators? If there is any gain, is it worth the hassle of more complex implementation over just declaring your comparator when you declare your type?
...Comparing to Haskell with it's type classes... .
Comparing to SML and OCaml with it's functors...
With F# I'm very interested in both the theoretical and pragmatic concerns, and ultimately there are some tradeoffs here.
My advice for accessible, reusable data structures is "sensible defaults, ultimate control". That is, provide default constructors based on structural versions of compare/hash, and then provide additional constructors that permit explicit configuration of compare/hash. This is the style followed by the Microsoft.FSharp.Collections.HashTable implementation and the other F# collections (sets, maps, hash sets) will go the same way over time. It is also roughly the style followed by the .NET collections, with the exception that the default comparison used by the .NET collections is not exactly F# structural comparison, but an approximation of it - e.g. it won't structurally compare arrays, though will structurally compare tuples and all other F# data types.
Re functors and type classes:
I know this makes the data structures slightly longer to write than when using type classes (since you have to pass the compare/hash explicitly). However, while I love type classes and am considering them for F#, they can lead to problems with flexibility - if you want to use a different comparison function you're a bit stuck.
So I advocate the tasteful application of the "sensible defaults, ultimate control" approach to give a good balance: a simple way to get going with using the data structure (beginners can just start using the data structure on simple types without problems), and it gives experienced users the flexibility they need (e.g. case-invariant comparison on strings is often required).