I want to use a typeclass to return a String
instance that is functionally dependent upon a Haskell type. For example, imagine we have the type Form
. I want to associate the string "form" with this type. Given the type Invocation
, I want to associate the string "job". And so on. The important thing is that I will often not have an instance of the type in question, though the type will be in the type signature of any function that uses it.
Perhaps an easy way to demonstrate this is with Swift:
protocol Resource {
static var resourcePathSegment: String { get }
}
struct Form : Resource {
static let resourcePathSegment = "form"
}
struct Invocation: Resource {
static let resourcePathSegment = "job"
}
print(Form.resourcePathSegment)
Why would I want such a thing? I'm talking to a REST API that uses certain conventions. For a GET
request, the path segment to the resource in question is functionally dependent upon the type of the data returned from the request. However, I won't have an instance of this type until the request completes.
Using TypeApplications
, this is easy:
{-# LANGUAGE AllowAmbiguousTypes, TypeApplications #-}
class Resource a where
resourcePathSegment :: String
instance Resource Form where
resourcePathSegment = "form"
instance Resource Invocation where
resourcePathSegment = "job"
To illustrate how to use the above:
ghci> :t resourcePathSegment
resourcePathSegment :: Resource a => String
ghci> resourcePathSegment @Form
"form"
ghci> resourcePathSegment @Invocation
"job"
The Proxy
type, from Data.Proxy
, is defined as data Proxy a = Proxy
.
This may be used to create a typeclass for this functionality:
class Resource a where
name :: Proxy a -> String
instance Resource Form
name _ = "form"
instance Resource Invocation where
name _ = "job"
-- etc.
You can then get the name
of a type by calling name (Proxy :: Proxy type_name)
.
There are several variations of this technique; an excellent summary can be found at the recent blog post Proxy arguments in class methods: a comparative analysis.
EDIT: I see that @Alexis King has submitted a very similar answer. Refer to the blog post above for an overview of the pros and cons of each approach. (Personally, I would use Alexis's technique, since I like TypeApplications
, but I feel that mine is a bit simpler since it doesn't require AllowAmbiguousTypes
or TypeApplications
to use.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With