Поясните для тупых короч
Дано:
data Req = A | B
data Resp = Ra | Rb | Rc
Куда нажать, чтобы можно было писать только функции A -> Ra, B -> Rb, B -> Rc, а A -> Rb и A -> Rc было писать нельзя?
@qnikst data TA
data TB
data Req where
A :: Req TA
B :: Req TB
data Resp where
Ra :: Resp TA
Rb :: Resp TB
Rc :: Resp TB
Но в жизни все сложнее и если хочешь разобраться то читай статьи по session types.
@qnikst >We describe an implementation of session types in Haskell. Session types statically enforce that client-server communication proceeds according to protocols.
Exactly what i want! Спасибо!
@plhk Session types почти всегда оверкил, особенно если не хочешь зашивать действительно сложную логику с циклами и условиями. Из неполных решений что я видел это: а. отсутствие решения, б. вместо АДТ под все запросы АДТ под каждый запрос ответ и дальше или тайпкласс или заворачивание в АДТ/ГАДТ. Четкого рецепта подсказать не могу у всего есть плюсы и минусы.
@kb Не не не Девид Блейн.. Ты или data R = A | B используй или втыкай
type family M Rep Resp :: () where
M A RA = ()
M B Rb = ()
M B Rc = ()
protocol :: (Matches a b) => Req a -> (Resp b -> c)
@qnikst Уау, круто. Не знал, что можно `(Matches a b) =>` на семействах типов делать, спасибо. Подобное сделано, только на тайп-классах, в Riak-библиотеке (то ли в протобуфах, не помню).
@kb Да, на тайпклассах было чот в протобуфовом интерфейсе к БД какой-то. Но там халява т.к. request однозначно определял reply в итоге все прекрасно сходилось и встраивалось в netwire. Я пытался по образу и подобию сделать чуть более сложный случай и все ломалось к чертям // может не умел тогда просто.
@qnikst Ну вот это на тайп фемилиес оно и есть, кажется. Или нет?
А я же просто сбился и о своих каких-то проблемах начал городить, т.к. у меня еще часто надобность писать что-то, что принимает в параметр только именно `A`, например (и не обязательно возвращает `Ra`). Короче не туда)
Справедливости ради, вот запилил пример на тайп-классах, который должен быть понятен начинающему:
```
{-# LANGUAGE MultiParamTypeClasses #-}
class Req a
instance Req A
instance Req B
class Resp b
instance Resp Ra
instance Resp Rb
instance Resp Rc
class ReqResp a b
instance ReqResp A Ra
instance ReqResp B Rb
data A = A
data B = B
data Ra = Ra
data Rb = Rb
data Rc = Rc
sendA :: ReqResp a b => a -> b
sendA = undefined
unSendA :: ReqResp a b => b -> a
unSendA = undefined
main :: IO ()
main = do
let ra = sendA A :: Ra
let a = unSendA ra :: A
putStrLn "hello"
```
Как видно, проблема сделать `Req = A | B` встает потому, что `A` и `B` в таком случае имеют один тип. Решается извращениями с DataKinds.