Как указано, это как раз работа для классов типов. Типовой класс - это конструкция, которая позволяет вам объединить ie несколько типов, а также определить некоторые функции для этих типов.
В вашем случае мы можем определить класс Handler
следующим образом:
class Handler request response where
handle :: request -> response
Здесь request
и response
- это переменные типа, представляющие два типа, которые «связываются вместе» нашим классом, и функция handle
, которая принимает один и возвращает другой.
Затем мы можем определить экземпляры этого класса для ваших двух случаев:
instance Handler FetchAccessories AccessoriesResponse where
handle FetchAccessories = AccessoriesResponse
instance Handler FetchProducts ProductsResponse where
handle FetchProducts = ProductsResponse
И затем мы можем использовать функцию:
someFn :: AccessoriesResponse
someFn = handle FetchAccessories
(обратите внимание, что вы необходимо включить MultiParamTypeClasses
, чтобы это работало)
В ответ на ваш комментарий: Интересно, есть ли способ избежать аннотации в someFn (поскольку gh c кажется очень близким к знанию)
Проблема в том, что GH C на самом деле не близок к знанию. Вы знаете, что AccessoriesResponse
идет только с FetchAccessories
, но что касается GH C, это не обязательно так. В конце концов, вы можете go вперед и добавить еще один экземпляр класса, например:
instance Handler FetchAccessories String where
handler FetchAccessories = "foo"
И теперь оказывается, что handle FetchAccessories
может означать либо AccessoriesResponse
, либо "foo"
. GH C не может решить за вас.
Но вы можете явно сказать, что для каждого типа запроса может быть только один тип ответа. Это называется «функциональной зависимостью» (для этого необходимо включить FunctionalDependencies
), а его синтаксис следующий:
class Handler request response | request -> response where
handler :: request -> response
Это сообщит GH C, что response
однозначно определяется request
и будет иметь два практических последствия: (1) GH C отклонит второй экземпляр Handler FetchAccessories String
из моего примера выше, жалуясь, что он нарушает функциональную зависимость, и (2) GH C сможет выяснить, что такое response
, просто зная request
.
В частности, это означает, что вы можете опустить подпись типа на someFn
.
В соответствующем примечании вы можете или может не захотеть делать противоположное: на каждый ответ может быть только один запрос. Для этого вы можете указать две функциональные зависимости:
class Handler request response | request -> response, response -> request where
(приведенное ниже больше не актуально в свете вашего комментария, но я оставлю это здесь для протокола)
Однако я подозреваю , что на самом деле вы имели в виду модель другого типа. Я подозреваю, что вы имели в виду модель, в которой говорится: « запрос может быть либо для продуктов, либо для аксессуаров, а ответ может быть либо для продуктов, либо для аксессуаров, а функция handle
превратит любой запрос в соответствующий ответ"
Если это действительно то, что вы действительно имели в виду, подходящей моделью будут типы сумм:
data Fetch = FetchAccessories | FetchProducts
data Response = AccessoriesResponse | ProductsResponse
handle :: Fetch -> Response
handle FetchAccessories = AccessoriesResponse
handle FetchProducts = ProductsResponse