{"id":24278480,"url":"https://github.com/stevegilham/fsharp-typeclasses","last_synced_at":"2025-10-25T15:44:08.614Z","repository":{"id":80554716,"uuid":"41751402","full_name":"SteveGilham/fsharp-typeclasses","owner":"SteveGilham","description":"Automatically exported from code.google.com/p/fsharp-typeclasses","archived":false,"fork":false,"pushed_at":"2017-08-08T21:41:50.000Z","size":185,"stargazers_count":8,"open_issues_count":2,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-07T19:04:49.143Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"F#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SteveGilham.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-09-01T16:52:26.000Z","updated_at":"2021-07-23T05:38:04.000Z","dependencies_parsed_at":"2023-03-12T19:30:52.512Z","dependency_job_id":null,"html_url":"https://github.com/SteveGilham/fsharp-typeclasses","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/SteveGilham/fsharp-typeclasses","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SteveGilham%2Ffsharp-typeclasses","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SteveGilham%2Ffsharp-typeclasses/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SteveGilham%2Ffsharp-typeclasses/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SteveGilham%2Ffsharp-typeclasses/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SteveGilham","download_url":"https://codeload.github.com/SteveGilham/fsharp-typeclasses/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SteveGilham%2Ffsharp-typeclasses/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":276828566,"owners_count":25712006,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-24T02:00:09.776Z","response_time":97,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-16T00:48:42.239Z","updated_at":"2025-09-24T22:30:55.634Z","avatar_url":"https://github.com/SteveGilham.png","language":"F#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# About fsharp-typeclasses\n\nThis is a convenience/archival export of a library from GoogleCode.  The active successor project to this one is gusty's [FSharpPlus (F#+)](https://github.com/gusty/FSharpPlus) here on GitHub.\n\n## What license applies to this code\n\n[Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0)\n\n## Basic How-To\n\nThe original ReadMe.txt reads as follows\n\n\u003e To open this project:\n\u003e \n\u003e 1. Open and Compile /InlineHelper/InlineHelper.fsproj with F# 3.0 then copy InlineHelper.dll to /lib/ : Alternatively or if you don't have F# 3.0 you can download InlineHelper.dll from http://fsharp-typeclasses.googlecode.com/files/fsharp-typeclasses-r118.zip\n\u003e \n\u003e 2. Open the project file fsharp-typeclasses.fsproj\n\u003e\n\u003e 3. Have a look at Scripts.fsx for sample code.\n\n*Note:* The link to Google Code no longer functions, but then you should be using a more recent F# these days, anyway.\n\n# Overview\n\nEmulate Haskell's typeclasses in F#\n\nDemonstrate how using inline and operator overloading is possible to emulate Haskell's typeclasses in F#.\n\nThis project is based on this post http://nut-cracker.azurewebsites.net/index.php/typeclasses-for-fsharp\n\nThere is a project based on this approach https://github.com/gmpl/FsControl more specific to F# in general (and less tied to Haskell).\nSample code\n\n```\n\n    fmap ((+) 2) [1;2;3] ;; val it : int list = [3; 4; 5] fmap ((+) 2) (Some 3) ;; val it : int option = Some 5\n\n    let a:list\u003c_\u003e = return' 1 ;; val a : int list = [1]\n\n    let a:option\u003c_\u003e = return' 1 ;; val a : int option = Some 1\n\n    [(+) 10;(*) 2] \u003e\u003e= fun f -\u003e [f 1;f 2;f 3] ;; val it : int list = [11; 12; 13; 2; 4; 6]\n\n    return' ((+) 10) \u003e\u003e= fun f -\u003e Some (f 1) ;; val it : int option = Some 11\n\n    // F# // Haskell let result = do' { // do { let! x1 = [1;2] // x1 \u003c- [1;2] let! x2 = [10;20] // x2 \u003c- [10;20] return ((+) x1 x2) } // return ((+) x1 x2) } ;;\n\nval result : int list = [11; 21; 12; 22]\n\n```\n\n## Advantages (compared to other approaches)\n\n* Type-safe: no cast, unbox required.\n\n* Performance: no reflection or any kind of run-time mechanism involved. Everything happens at compile time.\n\n* Although declaring Typeclasses could be sometimes tricky, to use them is very straight forward and there is no code overhead when using existing type classes. It' s easy to run Haskell snippets with minimum syntax translation.\n\n* It's possible to implement Typeclasses which in Haskell involves Higher Kinds like Functor, Monad, Category and Arrow.\n\n* No need to recompile the code to add instances of a new type.\n\n## Limitations:\n\n* As this technique involves inline with static constraints, overloaded functions can be called only from F#.\n\n* To write Typeclasses using an intermediate DU with the ternary operator could be tricky for some cases and they should include at least two instances in order to make type inference guess the proper statically constrained type.\n\n* There is no real group of functions, they could work sometimes individually. Nothing prevents you from using an incomplete definition of the Typeclass. As an example you can have return' defined but not (\u003e\u003e=) and calling to return' would still work. In some cases this is considered more as a feature than a limitation.\n\n* Constraints of undecided types are not very readable.\n\n* Orphaned instances are not supported. Instances must be implemented either in the class or in the type. I'm not sure if it is possible or not to support orphaned instances but even in Haskell they are not a good practice.\n\n* ~~Default implementations are not supported, you have to explicitly specify which implementation you want for each function.~~ There is a way to implement defaults, it was implemented in [FsControl](https://github.com/gmpl/FsControl)\n\n* Compile time could be long depending on how the library is used. Here are some advices to improve compile time:\n\n    . Use the typeclass library as a compiled dll\n\n    . Avoid do notation when not really needed.\n\n    . Specifying type annotations will help type inference and thus reduce compile time.\n\n## Translating from Haskell\n\n```\n| Haskell                   | F#                |\n|:------------              |:-----------       |\n| functions                 |                   | \n| const                     | const'            | \n| return                    | return'           | \n| pure                      | pure'             | \n| show                      | string            | \n| Polymorphic constants     |                   | \n| mempty                    | mempty()          | \n| mzero                     | mzero()           | \n| id (arrows)               | id'()             | \n| Operators                 |                   | \n| $                         | \u003c|                | \n| .                         | \u003c\u003c                | \n| . (arrows)                | \u003c\u003c\u003c               | \n| ::                        | :                 | \n| /=                        | =/                | \n| |\u003c\u003c                       | \u003e                 | \n| ^                         | **^               | \n| ^^                        | **^^              | \n| Literals                  |                   | \n| 5                         | 5G                | \n| \"hello world\"             | !\"hello world\"    | \n| func                      |                   | \n| Do notation               |                   | \n| do                        | do' {...}         | \n| x \u003c- a                    | let! x = a        | \n| f x                       | do! f x           | \n| Do notation (last line)   |                   | \n| return a                  | return a          | \n| a                         | return! a         |\n```\n\n## *_NOTES_*\n\nTo improve readability I adopted some conventions:\n\nType Classes are created by defining one type for each member.\n\nThe type will be defined as a \"singleton\" Discriminated Union (DU) where the name is the same as the function but first letter is uppercase.\n\nThe operator will always be the ternary operator (?\u003c-) used for dynamic assignment.\n\n1st parameter: it's always the DU and will have the name of the Typeclass starting with an underscore.\n\nInput parameter: if used will be the 2nd. An input parameter of a polymorphic type.\n\nOutput parameter: In this case the parameter name will be an underscore and the return type specified.\n\nIf we didn't use an input parameter, we'll have to fill the 3rd parameter with an unused one.\n\nTo summarize, we can have two different cases: \n1.  `static member [inline] (?\u003c-) (TypeClassName:MemberName, _:ReturnType , _ )`\n2.  `static member [inline] (?\u003c-) (TypeClassName:MemberName, x:InputParam, _:ReturnType)`\n\n### Update\n\nSince ~~[revision 117](http://code.google.com/p/fsharp-typeclasses/source/detail?r=bfb011cdbd668c42a676b5d2f06a80fc8e6900d0)~~  [3960b8d](https://github.com/SteveGilham/fsharp-typeclasses/commit/3960b8d6ae3efa2af4ad9930cab73b3b59a3733b) the convention using the (?\u003c-) operator was discarded. Now the convention is to use a method named \"instance\" and specify all parameters you need in order, like this:\n\n`static member [inline] instance (TypeClassName:MemberName, p1:InputParam1, ... , pn:InputParamN, _:ReturnType )`\n\n## Alternative Solution\n\nUPDATE: This is no longer needed. The bug in the new F# version was fixed and I was able to compile a function that replace and extend the old use of the (?\u003c-) operator.\n\nOne frequent question is why do we need to use the ternary operator (?\u003c-).\n\nCan't we use just statics methods?\n\nThe answer is in theory yes, but there ~~is~~ was a bug in the F# parser that fails to parse more than 2 'or' expressions in the call to a method with a static constraint.\n\nThis may sound confusing, so here are two examples: \n\n`let inline return' x : ^R = ((^C or ^R) : (static member return' : ^C * ^R -\u003e _) (Monad, defaultof\u003c ^R\u003e) ) x`\n\n` let inline (\u003e\u003e=) x f : ^R = ((^C or ^a or ^R) : (static member bind : ^C * ^a * ^R -\u003e _) (Monad, x, defaultof\u003c ^R\u003e)) f`\n\nThe first line will work, because it involves only two terms inside the 'or' expression but the second line will fail because it involves 3.\n\nThe [alternative solution](https://github.com/SteveGilham/fsharp-typeclasses.staticmethods) shows how the code will looks like if that bug is fixed, and it includes a fix for the F# compiler, so we can recompile F# and use it to compile the alternative solution.\n\nSo that's why in the default version we use (?\u003c-), because with operators the 'or' expression is inferred, and there is no need to put it explicitly which will fail to parse.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevegilham%2Ffsharp-typeclasses","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstevegilham%2Ffsharp-typeclasses","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevegilham%2Ffsharp-typeclasses/lists"}