This question seems really stupid but I can't find anything similar online. So here I go.
I need to use a Collection-like structure that should be used as values inside a Map. This collection has a generic type and the map should contain any kind of these collections (of int, boolean, string, etc). Given that this is not possible directly (because of the generic type constrained to a unique value in the Map definition) I used an auxiliary DU to match all the cases given that they are few. My problem is that I want to write a generic function that receives a HOF and execute on the Collection-like data structure regardless of the internal generic type... but I can't; this is the simple code to exemplify the problem:
type EspecificList =
| IntList of int list
| BoolList of bool list
let listLength = function
| IntList list -> List.length list
| BoolList list -> List.length list
let listGenericFunc func = function
| IntList list -> func list
| BoolList list -> func list
listLength work as expected but listGenericFunc doesn't; func
is specialized to the first type and an error sows on the second one: adding a generic type annotation like (func: List<'a> -> 'b)
doesn't work either.
Is there any way to avoid this and maintain the generic spirit of the function? I'm probably missing something really obvious but I can't see it.
Thanks in advance!
Edit
Okay so, I continued my research for the last couple of days and although I'm planning to submit a more detailed question about my particular domain problem I want to share the possible solutions that I encountered and ask If you think that any of them is better or more FSharpish. I'll list the solutions in the order that I found them.
1. WalternativE response
Passing the function n times one per possible type; with or without the type annotations
let listGenericFunc (func: bool list -> 'b) (func': int list -> 'b) = function
| BoolList list -> func list
| IntList list -> func' list
listGenericFunc List.length List.length (IntList [1;2])
2. Static Member and Inline magic
Using a static 'apply' member and define types for every possible function in conjunction with an 'applyer' inlined one.
type ListLength = ListLength with static member ( $ ) (ListLength, x) = x |> List.length
let inline applier f x = f $ x
let listGenericFuncInline func = function
| IntList list -> applier func list
| BoolList list -> applier func list
listGenericFuncInline ListLength (IntList [1; 2; 3]) // return 3
listGenericFuncInline ListLength (BoolList [true; false]) // return 2
It was a response to this particular SO question
3. Hidden Generic Type
From the last question, I found Existential types and searching a little I stumble upon this article. Using only the first part of the post about Universal types makes it possible to accomplish what I want.
type UListFuncs<'ret> = abstract member Eval<'a> : ('a list) -> 'ret
let listLength : UListFuncs<int> =
{ new UListFuncs<int> with
member __.Eval<'a> (x : 'a list) = x |> List.length }
let listGenericFuncUniversal (func : UListFuncs<'a>) = function
| IntList list -> func.Eval list
| BoolList list -> func.Eval list
listGenericFuncUniversal listLength (IntList [1; 2; 3]) // return 3
listGenericFuncUniversal listLength (BoolList [true; false]) // return 2
Impressions
I don't know which of them is the best alternative; I feel that the second one is a bit awkward because of the type needed for every function; I really like the third regardless of the added boilerplate (The article is well explained and very interesting to read too). What are your thoughts?