2. Current state of affairs
• Web programming is done in dynamic
languages
• There is no competing Haskell framework
• There are interesting FP techniques waiting
to be applied
3. Using Haskell for web
programming
• Generic Programming
• Type inferencing
• Lenses
• Type-level programming
4. The Pesto Framework
Three libraries for programming the web
• A library to store data
• A library to display data
• A library for control flow
6. The Quiz Code takeQuiz :: Web (Ref QuizModel Quiz) ()
takeQuiz = proc ref -> do
quiz <- basil find -< ref
addQuiz :: Web () () case quiz of
instance Formlet Email where formlet x = Email <$> formlet
addQuiz = proc () -> do Just quiz -> do
(unEmail <$> x)
q <- input -< Just qsR <- basil (findRels DL ixQuestions) -< ref
data Quiz = Quiz { subject :: String,
() qs <- basil (mapM find) -<
description :: String}
ref <- (basil (x -> new ixQuiz x PNil)) -< S.toList qsR
data Question = Question { title :: String, choiceA,
q () <- display ghtml -< quiz
choiceB, choiceC :: String}
_ <- addQuestions -< response <- form'' responseForm -< ()
data Response = Response { name :: String
ref as <- many getAnswer -<
, email :: Email
display (const $ X.toHtml "added quiz with questions.") -< (catMaybes qs)
, date :: Date
() display ghtml -<
, answers :: [Answer]
addQuestions :: Web (Ref QuizModel Quiz) ([Ref QuizModel response {answers = as}
}
Question]) Nothing -> display (const "Not Found") -< ()
data Answer = QA | QB | QC deriving (Show, Eq, Enum)
addQuestions = proc q -> do data ResponseView = ResponseView { _name :: String ,
$(mkTList "QuizModel" [''Quiz, ''Question, ''Response])
qs <- inputMany -< () _email :: Email }
type Questions = Rel QuizModel One Quiz Many Question
basil ((qs,qz) -> mapM (newQuestion qz) qs) -< (qs, q) responseProj :: Response :-> ResponseView
type Responses = Rel QuizModel One Quiz Many Response
newQuestion :: Ref QuizModel Quiz -> Question -> M (Ref responseProj = Lens $ ResponseView <$> _name `for` lName <*>
questions :: Questions
QuizModel Question) _email `for` lEmail
questions = Rel One ixQuiz "quiz" Many ixQuestion
newQuestion refQuiz question = new ixQuestion responseForm :: Form Response
"questions"
question responseForm = projectedForm responseProj emptyResponse
responses :: Responses
(PCons (refQuiz, DR, where emptyResponse :: Response
responses = Rel One ixQuiz "quiz" Many ixResponse
ixQuestions) PNil) emptyResponse = Response "" (Email "") (Date "now")
"responses"
listQuizzes :: Web () () []
$(mkTlist "QuizRelations" [''Questions, ''Responses])
listQuizzes = proc () -> do getAnswer :: Web Question Answer
instance ERModel QuizModel QuizRelations where
qs <- basil' (findAll ixQuiz) -< () getAnswer = form''' answerForm
relations = TCons4 questions $ TCons4 responses $ TNil4
case qs of answerForm :: Question -> Form Answer
witnesses = WCons ixQuiz $ WCons ixQuestion $ WCons
[] -> display X.toHtml -< "No answerForm q = F.plug ((title q +++ X.br) +++)
ixResponse $ WNil
quizzes yet." $ F.enumRadio [(QA, choiceA q),(QB, choiceB
$(deriveAll ''Quiz "PFQuiz")
_ -> display (X.concatHtml . map quizWithLink) -< qs q),(QC, choiceC q)]
$(deriveAll ''Question "PFQuestion")
quizWithLink :: (Ref QuizModel Quiz, Quiz) -> X.Html Nothing
$(deriveAll ''Response "PFResponse")
quizWithLink (Ref _ i, q) = X.concatHtml homepage = X.toHtml "Welcome to the quiz system."
type instance PF Quiz = PFQuiz
[ ghtml q, X.br, static (View i) "view" data QuizForm = QuizForm {_subject :: String, _description ::
type instance PF Question = PFQuestion
, X.br, static (Take i) "take quiz" ] TextArea}
type instance PF Response = PFResponse
viewQuiz :: Web (Ref QuizModel Quiz) () quizProj :: Quiz :-> QuizForm
$(mkLabels [''Quiz, ''Question, ''Response])
viewQuiz = proc ref -> do quizProj = Lens $ QuizForm <$> _subject `for` lSubject
$(mkEqualities [''Quiz, ''Question, ''Response])
quiz <- basil find -< ref <*> _description `for`
data QuizRoute = List | Add | View Int | Take Int
case quiz of (textAreaToString % lDescription)
deriving Show
Just quiz -> do instance DefaultForm Quiz where form = projectedForm
instance ToURL QuizRoute where
Just qsR <- basil (findRels DL ixQuestions) -< ref quizProj (Quiz "" "")
toURL = gtoURL . from
qs <- basil (mapM find) -< instance DefaultForm Question where form = gform Nothing
fromURL = fmap to . gfromURL
S.toList qsR type M a = Basil QuizModel QuizRelations a
$(deriveAll ''QuizRoute "PFQuizRoute")
display quizWithQuestions -< (ref, type St = BasilState QuizModel QuizRelations
type instance PF QuizRoute = PFQuizRoute
quiz, catMaybes qs) type Web i o = WebT (MVar St) IO i o
static :: QuizRoute -> String -> X.Html
Nothing -> display (const "Not Found") -< () template x = X.thehtml ( (X.header $ linkStyle "/public/
static u s = X.anchor X.! [X.href $ "/" ++ renderURL (toURL
quizWithQuestions :: (Ref QuizModel Quiz, Quiz, [Question]) - style.css")
u)] X.<< X.toHtml s
> X.Html +++ (X.body $ templateHeader +++ x))
handle :: QuizRoute -> Continuation (MVar St) IO ()
quizWithQuestions (Ref _ i, q, qs) = ghtml q templateHeader = (X.h1 X.! [X.theclass "page"]) (X.toHtml
handle Add = Cont () addQuiz
+++ X.br "Quiz system") +++ menu
handle List = Cont () listQuizzes
+++ (X.concatHtml $ intersperse menu = X.unordList X.! [X.theclass "menu"] $ [static Add "add
handle (View i) = Cont (Ref ixQuiz i) viewQuiz
X.br $ (map ghtml) qs) quiz", static List "list quizzes"]
handle (Take i) = Cont (Ref ixQuiz i) takeQuiz
+++ static (Take i) "take quiz" linkStyle name = (X.thelink X.! [X.rel "stylesheet", X.href
name]) X.noHtml
$(deriveAll ''QuizForm "PFQuizForm")
type instance PF QuizForm = PFQuizForm
$(deriveAll ''ResponseView "PFResponseView")
7. The Quiz Code takeQuiz :: Web (Ref QuizModel Quiz) ()
takeQuiz = proc ref -> do
quiz <- basil find -< ref
addQuiz :: Web () () case quiz of
instance Formlet Email where formlet x = Email <$> formlet
addQuiz = proc () -> do Just quiz -> do
(unEmail <$> x)
q <- input -< Just qsR <- basil (findRels DL ixQuestions) -< ref
data Quiz = Quiz { subject :: String,
() qs <- basil (mapM find) -<
description :: String}
ref <- (basil (x -> new ixQuiz x PNil)) -< S.toList qsR
data Question = Question { title :: String, choiceA,
q () <- display ghtml -< quiz
choiceB, choiceC :: String}
_ <- addQuestions -< response <- form'' responseForm -< ()
data Response = Response { name :: String
ref as <- many getAnswer -<
, email :: Email
display (const $ X.toHtml "added quiz with questions.") -< (catMaybes qs)
, date :: Date
() display ghtml -<
, answers :: [Answer]
addQuestions :: Web (Ref QuizModel Quiz) ([Ref QuizModel response {answers = as}
}
Question]) Nothing -> display (const "Not Found") -< ()
data Answer = QA | QB | QC deriving (Show, Eq, Enum)
addQuestions = proc q -> do data ResponseView = ResponseView { _name :: String ,
$(mkTList "QuizModel" [''Quiz, ''Question, ''Response])
qs <- inputMany -< () _email :: Email }
type Questions = Rel QuizModel One Quiz Many Question
basil ((qs,qz) -> mapM (newQuestion qz) qs) -< (qs, q) responseProj :: Response :-> ResponseView
type Responses = Rel QuizModel One Quiz Many Response
newQuestion :: Ref QuizModel Quiz -> Question -> M (Ref responseProj = Lens $ ResponseView <$> _name `for` lName <*>
questions :: Questions
Model code
QuizModel Question) _email `for` lEmail
questions = Rel One ixQuiz "quiz" Many ixQuestion
newQuestion refQuiz question = new ixQuestion responseForm :: Form Response
"questions"
question responseForm = projectedForm responseProj emptyResponse
responses :: Responses
(PCons (refQuiz, DR, where emptyResponse :: Response
responses = Rel One ixQuiz "quiz" Many ixResponse
ixQuestions) PNil) emptyResponse = Response "" (Email "") (Date "now")
"responses"
listQuizzes :: Web () () []
$(mkTlist "QuizRelations" [''Questions, ''Responses])
listQuizzes = proc () -> do getAnswer :: Web Question Answer
instance ERModel QuizModel QuizRelations where
qs <- basil' (findAll ixQuiz) -< () getAnswer = form''' answerForm
relations = TCons4 questions $ TCons4 responses $ TNil4
case qs of answerForm :: Question -> Form Answer
witnesses = WCons ixQuiz $ WCons ixQuestion $ WCons
[] -> display X.toHtml -< "No answerForm q = F.plug ((title q +++ X.br) +++)
ixResponse $ WNil
quizzes yet." $ F.enumRadio [(QA, choiceA q),(QB, choiceB
$(deriveAll ''Quiz "PFQuiz")
_ -> display (X.concatHtml . map quizWithLink) -< qs q),(QC, choiceC q)]
$(deriveAll ''Question "PFQuestion")
quizWithLink :: (Ref QuizModel Quiz, Quiz) -> X.Html Nothing
$(deriveAll ''Response "PFResponse")
quizWithLink (Ref _ i, q) = X.concatHtml homepage = X.toHtml "Welcome to the quiz system."
type instance PF Quiz = PFQuiz
[ ghtml q, X.br, static (View i) "view" data QuizForm = QuizForm {_subject :: String, _description ::
type instance PF Question = PFQuestion
, X.br, static (Take i) "take quiz" ] TextArea}
type instance PF Response = PFResponse
viewQuiz :: Web (Ref QuizModel Quiz) () quizProj :: Quiz :-> QuizForm
$(mkLabels [''Quiz, ''Question, ''Response])
viewQuiz = proc ref -> do quizProj = Lens $ QuizForm <$> _subject `for` lSubject
$(mkEqualities [''Quiz, ''Question, ''Response])
quiz <- basil find -< ref <*> _description `for`
data QuizRoute = List | Add | View Int | Take Int
case quiz of (textAreaToString % lDescription)
deriving Show
Just quiz -> do instance DefaultForm Quiz where form = projectedForm
instance ToURL QuizRoute where
Just qsR <- basil (findRels DL ixQuestions) -< ref quizProj (Quiz "" "")
toURL = gtoURL . from
qs <- basil (mapM find) -< instance DefaultForm Question where form = gform Nothing
fromURL = fmap to . gfromURL
S.toList qsR type M a = Basil QuizModel QuizRelations a
$(deriveAll ''QuizRoute "PFQuizRoute")
display quizWithQuestions -< (ref, type St = BasilState QuizModel QuizRelations
type instance PF QuizRoute = PFQuizRoute
quiz, catMaybes qs) type Web i o = WebT (MVar St) IO i o
static :: QuizRoute -> String -> X.Html
Nothing -> display (const "Not Found") -< () template x = X.thehtml ( (X.header $ linkStyle "/public/
static u s = X.anchor X.! [X.href $ "/" ++ renderURL (toURL
quizWithQuestions :: (Ref QuizModel Quiz, Quiz, [Question]) - style.css")
u)] X.<< X.toHtml s
> X.Html +++ (X.body $ templateHeader +++ x))
handle :: QuizRoute -> Continuation (MVar St) IO ()
quizWithQuestions (Ref _ i, q, qs) = ghtml q templateHeader = (X.h1 X.! [X.theclass "page"]) (X.toHtml
handle Add = Cont () addQuiz
+++ X.br "Quiz system") +++ menu
handle List = Cont () listQuizzes
+++ (X.concatHtml $ intersperse menu = X.unordList X.! [X.theclass "menu"] $ [static Add "add
handle (View i) = Cont (Ref ixQuiz i) viewQuiz
X.br $ (map ghtml) qs) quiz", static List "list quizzes"]
handle (Take i) = Cont (Ref ixQuiz i) takeQuiz
+++ static (Take i) "take quiz" linkStyle name = (X.thelink X.! [X.rel "stylesheet", X.href
name]) X.noHtml
$(deriveAll ''QuizForm "PFQuizForm")
type instance PF QuizForm = PFQuizForm
$(deriveAll ''ResponseView "PFResponseView")
8. The Quiz Code takeQuiz :: Web (Ref QuizModel Quiz) ()
takeQuiz = proc ref -> do
quiz <- basil find -< ref
addQuiz :: Web () () case quiz of
instance Formlet Email where formlet x = Email <$> formlet
addQuiz = proc () -> do Just quiz -> do
(unEmail <$> x)
q <- input -< Just qsR <- basil (findRels DL ixQuestions) -< ref
data Quiz = Quiz { subject :: String,
() qs <- basil (mapM find) -<
description :: String}
ref <- (basil (x -> new ixQuiz x PNil)) -< S.toList qsR
data Question = Question { title :: String, choiceA,
q () <- display ghtml -< quiz
choiceB, choiceC :: String}
_ <- addQuestions -< response <- form'' responseForm -< ()
data Response = Response { name :: String
ref as <- many getAnswer -<
, email :: Email
display (const $ X.toHtml "added quiz with questions.") -< (catMaybes qs)
, date :: Date
() display ghtml -<
, answers :: [Answer]
addQuestions :: Web (Ref QuizModel Quiz) ([Ref QuizModel response {answers = as}
}
Question]) Nothing -> display (const "Not Found") -< ()
data Answer = QA | QB | QC deriving (Show, Eq, Enum)
addQuestions = proc q -> do data ResponseView = ResponseView { _name :: String ,
$(mkTList "QuizModel" [''Quiz, ''Question, ''Response])
qs <- inputMany -< () _email :: Email }
type Questions = Rel QuizModel One Quiz Many Question
basil ((qs,qz) -> mapM (newQuestion qz) qs) -< (qs, q) responseProj :: Response :-> ResponseView
type Responses = Rel QuizModel One Quiz Many Response
newQuestion :: Ref QuizModel Quiz -> Question -> M (Ref responseProj = Lens $ ResponseView <$> _name `for` lName <*>
questions :: Questions
Model code
QuizModel Question) _email `for` lEmail
questions = Rel One ixQuiz "quiz" Many ixQuestion
newQuestion refQuiz question = new ixQuestion responseForm :: Form Response
"questions"
question responseForm = projectedForm responseProj emptyResponse
responses :: Responses
(PCons (refQuiz, DR, where emptyResponse :: Response
responses = Rel One ixQuiz "quiz" Many ixResponse
ixQuestions) PNil) emptyResponse = Response "" (Email "") (Date "now")
"responses"
listQuizzes :: Web () () []
$(mkTlist "QuizRelations" [''Questions, ''Responses])
listQuizzes = proc () -> do getAnswer :: Web Question Answer
instance ERModel QuizModel QuizRelations where
qs <- basil' (findAll ixQuiz) -< () getAnswer = form''' answerForm
relations = TCons4 questions $ TCons4 responses $ TNil4
case qs of answerForm :: Question -> Form Answer
witnesses = WCons ixQuiz $ WCons ixQuestion $ WCons
[] -> display X.toHtml -< "No answerForm q = F.plug ((title q +++ X.br) +++)
ixResponse $ WNil
quizzes yet." $ F.enumRadio [(QA, choiceA q),(QB, choiceB
$(deriveAll ''Quiz "PFQuiz")
_ -> display (X.concatHtml . map quizWithLink) -< qs q),(QC, choiceC q)]
$(deriveAll ''Question "PFQuestion")
quizWithLink :: (Ref QuizModel Quiz, Quiz) -> X.Html Nothing
$(deriveAll ''Response "PFResponse")
quizWithLink (Ref _ i, q) = X.concatHtml homepage = X.toHtml "Welcome to the quiz system."
type instance PF Quiz = PFQuiz
[ ghtml q, X.br, static (View i) "view" data QuizForm = QuizForm {_subject :: String, _description ::
type instance PF Question = PFQuestion
, X.br, static (Take i) "take quiz" ] TextArea}
type instance PF Response = PFResponse
viewQuiz :: Web (Ref QuizModel Quiz) () quizProj :: Quiz :-> QuizForm
$(mkLabels [''Quiz, ''Question, ''Response])
viewQuiz = proc ref -> do quizProj = Lens $ QuizForm <$> _subject `for` lSubject
$(mkEqualities [''Quiz, ''Question, ''Response])
quiz <- basil find -< ref <*> _description `for`
data QuizRoute = List | Add | View Int | Take Int
case quiz of (textAreaToString % lDescription)
deriving Show
Just quiz -> do instance DefaultForm Quiz where form = projectedForm
instance ToURL QuizRoute where
Just qsR <- basil (findRels DL ixQuestions) -< ref quizProj (Quiz "" "")
toURL = gtoURL . from
qs <- basil (mapM find) -< instance DefaultForm Question where form = gform Nothing
fromURL = fmap to . gfromURL
S.toList qsR type M a = Basil QuizModel QuizRelations a
$(deriveAll ''QuizRoute "PFQuizRoute")
display quizWithQuestions -< (ref, type St = BasilState QuizModel QuizRelations
type instance PF QuizRoute = PFQuizRoute
quiz, catMaybes qs) type Web i o = WebT (MVar St) IO i o
static :: QuizRoute -> String -> X.Html
Nothing -> display (const "Not Found") -< () template x = X.thehtml ( (X.header $ linkStyle "/public/
static u s = X.anchor X.! [X.href $ "/" ++ renderURL (toURL
URL handling
quizWithQuestions :: (Ref QuizModel Quiz, Quiz, [Question]) - style.css")
u)] X.<< X.toHtml s
> X.Html +++ (X.body $ templateHeader +++ x))
handle :: QuizRoute -> Continuation (MVar St) IO ()
quizWithQuestions (Ref _ i, q, qs) = ghtml q templateHeader = (X.h1 X.! [X.theclass "page"]) (X.toHtml
handle Add = Cont () addQuiz
+++ X.br "Quiz system") +++ menu
handle List = Cont () listQuizzes
+++ (X.concatHtml $ intersperse menu = X.unordList X.! [X.theclass "menu"] $ [static Add "add
handle (View i) = Cont (Ref ixQuiz i) viewQuiz
X.br $ (map ghtml) qs) quiz", static List "list quizzes"]
handle (Take i) = Cont (Ref ixQuiz i) takeQuiz
+++ static (Take i) "take quiz" linkStyle name = (X.thelink X.! [X.rel "stylesheet", X.href
name]) X.noHtml
$(deriveAll ''QuizForm "PFQuizForm")
type instance PF QuizForm = PFQuizForm
$(deriveAll ''ResponseView "PFResponseView")
9. The Quiz Code takeQuiz :: Web (Ref QuizModel Quiz) ()
takeQuiz = proc ref -> do
quiz <- basil find -< ref
addQuiz :: Web () () case quiz of
instance Formlet Email where formlet x = Email <$> formlet
addQuiz = proc () -> do Just quiz -> do
(unEmail <$> x)
q <- input -< Just qsR <- basil (findRels DL ixQuestions) -< ref
data Quiz = Quiz { subject :: String,
() qs <- basil (mapM find) -<
description :: String}
ref <- (basil (x -> new ixQuiz x PNil)) -< S.toList qsR
data Question = Question { title :: String, choiceA,
q () <- display ghtml -< quiz
choiceB, choiceC :: String}
_ <- addQuestions -< response <- form'' responseForm -< ()
data Response = Response { name :: String
ref as <- many getAnswer -<
, email :: Email
display (const $ X.toHtml "added quiz with questions.") -< (catMaybes qs)
, date :: Date
() display ghtml -<
, answers :: [Answer]
addQuestions :: Web (Ref QuizModel Quiz) ([Ref QuizModel response {answers = as}
}
Question]) Nothing -> display (const "Not Found") -< ()
data Answer = QA | QB | QC deriving (Show, Eq, Enum)
addQuestions = proc q -> do data ResponseView = ResponseView { _name :: String ,
$(mkTList "QuizModel" [''Quiz, ''Question, ''Response])
qs <- inputMany -< () _email :: Email }
type Questions = Rel QuizModel One Quiz Many Question
basil ((qs,qz) -> mapM (newQuestion qz) qs) -< (qs, q) responseProj :: Response :-> ResponseView
type Responses = Rel QuizModel One Quiz Many Response
newQuestion :: Ref QuizModel Quiz -> Question -> M (Ref responseProj = Lens $ ResponseView <$> _name `for` lName <*>
questions :: Questions
Model code
QuizModel Question) _email `for` lEmail
Controller + View
questions = Rel One ixQuiz "quiz" Many ixQuestion
newQuestion refQuiz question = new ixQuestion responseForm :: Form Response
"questions"
question responseForm = projectedForm responseProj emptyResponse
responses :: Responses
(PCons (refQuiz, DR, where emptyResponse :: Response
responses = Rel One ixQuiz "quiz" Many ixResponse
ixQuestions) PNil) emptyResponse = Response "" (Email "") (Date "now")
"responses"
listQuizzes :: Web () () []
$(mkTlist "QuizRelations" [''Questions, ''Responses])
listQuizzes = proc () -> do getAnswer :: Web Question Answer
instance ERModel QuizModel QuizRelations where
qs <- basil' (findAll ixQuiz) -< () getAnswer = form''' answerForm
relations = TCons4 questions $ TCons4 responses $ TNil4
case qs of answerForm :: Question -> Form Answer
witnesses = WCons ixQuiz $ WCons ixQuestion $ WCons
[] -> display X.toHtml -< "No answerForm q = F.plug ((title q +++ X.br) +++)
ixResponse $ WNil
$(deriveAll ''Quiz "PFQuiz")
$(deriveAll ''Question "PFQuestion")
_ Controller + View
quizzes yet."
-> display (X.concatHtml . map quizWithLink) -< qs
quizWithLink :: (Ref QuizModel Quiz, Quiz) -> X.Html
$ F.enumRadio [(QA, choiceA q),(QB, choiceB
q),(QC, choiceC q)]
Nothing
$(deriveAll ''Response "PFResponse")
quizWithLink (Ref _ i, q) = X.concatHtml homepage = X.toHtml "Welcome to the quiz system."
type instance PF Quiz = PFQuiz
[ ghtml q, X.br, static (View i) "view" data QuizForm = QuizForm {_subject :: String, _description ::
type instance PF Question = PFQuestion
, X.br, static (Take i) "take quiz" ] TextArea}
type instance PF Response = PFResponse
viewQuiz :: Web (Ref QuizModel Quiz) () quizProj :: Quiz :-> QuizForm
$(mkLabels [''Quiz, ''Question, ''Response])
viewQuiz = proc ref -> do quizProj = Lens $ QuizForm <$> _subject `for` lSubject
$(mkEqualities [''Quiz, ''Question, ''Response])
quiz <- basil find -< ref <*> _description `for`
data QuizRoute = List | Add | View Int | Take Int
case quiz of (textAreaToString % lDescription)
deriving Show
Just quiz -> do instance DefaultForm Quiz where form = projectedForm
instance ToURL QuizRoute where
Just qsR <- basil (findRels DL ixQuestions) -< ref quizProj (Quiz "" "")
toURL = gtoURL . from
qs <- basil (mapM find) -< instance DefaultForm Question where form = gform Nothing
fromURL = fmap to . gfromURL
S.toList qsR type M a = Basil QuizModel QuizRelations a
$(deriveAll ''QuizRoute "PFQuizRoute")
display quizWithQuestions -< (ref, type St = BasilState QuizModel QuizRelations
type instance PF QuizRoute = PFQuizRoute
quiz, catMaybes qs) type Web i o = WebT (MVar St) IO i o
static :: QuizRoute -> String -> X.Html
Nothing -> display (const "Not Found") -< () template x = X.thehtml ( (X.header $ linkStyle "/public/
static u s = X.anchor X.! [X.href $ "/" ++ renderURL (toURL
URL handling
quizWithQuestions :: (Ref QuizModel Quiz, Quiz, [Question]) - style.css")
u)] X.<< X.toHtml s
> X.Html +++ (X.body $ templateHeader +++ x))
handle :: QuizRoute -> Continuation (MVar St) IO ()
quizWithQuestions (Ref _ i, q, qs) = ghtml q templateHeader = (X.h1 X.! [X.theclass "page"]) (X.toHtml
handle Add = Cont () addQuiz
+++ X.br "Quiz system") +++ menu
handle List = Cont () listQuizzes
+++ (X.concatHtml $ intersperse menu = X.unordList X.! [X.theclass "menu"] $ [static Add "add
handle (View i) = Cont (Ref ixQuiz i) viewQuiz
X.br $ (map ghtml) qs) quiz", static List "list quizzes"]
handle (Take i) = Cont (Ref ixQuiz i) takeQuiz
+++ static (Take i) "take quiz" linkStyle name = (X.thelink X.! [X.rel "stylesheet", X.href
name]) X.noHtml
$(deriveAll ''QuizForm "PFQuizForm")
type instance PF QuizForm = PFQuizForm
$(deriveAll ''ResponseView "PFResponseView")
10. The Quiz Code takeQuiz :: Web (Ref QuizModel Quiz) ()
takeQuiz = proc ref -> do
quiz <- basil find -< ref
addQuiz :: Web () () case quiz of
instance Formlet Email where formlet x = Email <$> formlet
addQuiz = proc () -> do Just quiz -> do
(unEmail <$> x)
q <- input -< Just qsR <- basil (findRels DL ixQuestions) -< ref
data Quiz = Quiz { subject :: String,
() qs <- basil (mapM find) -<
description :: String}
ref <- (basil (x -> new ixQuiz x PNil)) -< S.toList qsR
data Question = Question { title :: String, choiceA,
q () <- display ghtml -< quiz
choiceB, choiceC :: String}
_ <- addQuestions -< response <- form'' responseForm -< ()
data Response = Response { name :: String
ref as <- many getAnswer -<
, email :: Email
display (const $ X.toHtml "added quiz with questions.") -< (catMaybes qs)
, date :: Date
() display ghtml -<
, answers :: [Answer]
addQuestions :: Web (Ref QuizModel Quiz) ([Ref QuizModel response {answers = as}
}
Question]) Nothing -> display (const "Not Found") -< ()
data Answer = QA | QB | QC deriving (Show, Eq, Enum)
addQuestions = proc q -> do data ResponseView = ResponseView { _name :: String ,
$(mkTList "QuizModel" [''Quiz, ''Question, ''Response])
qs <- inputMany -< () _email :: Email }
type Questions = Rel QuizModel One Quiz Many Question
basil ((qs,qz) -> mapM (newQuestion qz) qs) -< (qs, q) responseProj :: Response :-> ResponseView
type Responses = Rel QuizModel One Quiz Many Response
newQuestion :: Ref QuizModel Quiz -> Question -> M (Ref responseProj = Lens $ ResponseView <$> _name `for` lName <*>
questions :: Questions
Model code
QuizModel Question) _email `for` lEmail
Controller + View
questions = Rel One ixQuiz "quiz" Many ixQuestion
newQuestion refQuiz question = new ixQuestion responseForm :: Form Response
"questions"
question responseForm = projectedForm responseProj emptyResponse
responses :: Responses
(PCons (refQuiz, DR, where emptyResponse :: Response
responses = Rel One ixQuiz "quiz" Many ixResponse
ixQuestions) PNil) emptyResponse = Response "" (Email "") (Date "now")
"responses"
listQuizzes :: Web () () []
$(mkTlist "QuizRelations" [''Questions, ''Responses])
listQuizzes = proc () -> do getAnswer :: Web Question Answer
instance ERModel QuizModel QuizRelations where
qs <- basil' (findAll ixQuiz) -< () getAnswer = form''' answerForm
relations = TCons4 questions $ TCons4 responses $ TNil4
case qs of answerForm :: Question -> Form Answer
witnesses = WCons ixQuiz $ WCons ixQuestion $ WCons
[] -> display X.toHtml -< "No answerForm q = F.plug ((title q +++ X.br) +++)
ixResponse $ WNil
$(deriveAll ''Quiz "PFQuiz")
$(deriveAll ''Question "PFQuestion")
_ Controller + View
quizzes yet."
-> display (X.concatHtml . map quizWithLink) -< qs
quizWithLink :: (Ref QuizModel Quiz, Quiz) -> X.Html
$ F.enumRadio [(QA, choiceA q),(QB, choiceB
q),(QC, choiceC q)]
Nothing
$(deriveAll ''Response "PFResponse")
quizWithLink (Ref _ i, q) = X.concatHtml homepage = X.toHtml "Welcome to the quiz system."
type instance PF Quiz = PFQuiz
[ ghtml q, X.br, static (View i) "view" data QuizForm = QuizForm {_subject :: String, _description ::
type instance PF Question = PFQuestion
, X.br, static (Take i) "take quiz" ] TextArea}
type instance PF Response = PFResponse
viewQuiz :: Web (Ref QuizModel Quiz) () quizProj :: Quiz :-> QuizForm
$(mkLabels [''Quiz, ''Question, ''Response])
viewQuiz = proc ref -> do quizProj = Lens $ QuizForm <$> _subject `for` lSubject
$(mkEqualities [''Quiz, ''Question, ''Response])
quiz <- basil find -< ref <*> _description `for`
data QuizRoute = List | Add | View Int | Take Int
case quiz of (textAreaToString % lDescription)
deriving Show
Just quiz -> do instance DefaultForm Quiz where form = projectedForm
instance ToURL QuizRoute where
Just qsR <- basil (findRels DL ixQuestions) -< ref quizProj (Quiz "" "")
toURL = gtoURL . from
qs <- basil (mapM find) -< instance DefaultForm Question where form = gform Nothing
fromURL = fmap to . gfromURL
S.toList qsR type M a = Basil QuizModel QuizRelations a
$(deriveAll ''QuizRoute "PFQuizRoute")
display quizWithQuestions -< (ref, type St = BasilState QuizModel QuizRelations
type instance PF QuizRoute = PFQuizRoute
quiz, catMaybes qs) type Web i o = WebT (MVar St) IO i o
static :: QuizRoute -> String -> X.Html
Nothing -> display (const "Not Found") -< () template x = X.thehtml ( (X.header $ linkStyle "/public/
static u s = X.anchor X.! [X.href $ "/" ++ renderURL (toURL
URL handling
quizWithQuestions :: (Ref QuizModel Quiz, Quiz, [Question]) - style.css")
u)] X.<< X.toHtml s
> X.Html +++ (X.body $ templateHeader +++ x))
handle :: QuizRoute -> Continuation (MVar St) IO ()
quizWithQuestions (Ref _ i, q, qs) = ghtml q
Miscellaneous
templateHeader = (X.h1 X.! [X.theclass "page"]) (X.toHtml
handle Add = Cont () addQuiz
+++ X.br "Quiz system") +++ menu
handle List = Cont () listQuizzes
+++ (X.concatHtml $ intersperse menu = X.unordList X.! [X.theclass "menu"] $ [static Add "add
handle (View i) = Cont (Ref ixQuiz i) viewQuiz
X.br $ (map ghtml) qs) quiz", static List "list quizzes"]
handle (Take i) = Cont (Ref ixQuiz i) takeQuiz
+++ static (Take i) "take quiz" linkStyle name = (X.thelink X.! [X.rel "stylesheet", X.href
name]) X.noHtml
$(deriveAll ''QuizForm "PFQuizForm")
type instance PF QuizForm = PFQuizForm
$(deriveAll ''ResponseView "PFResponseView")
11. Adding a Quiz
addQuiz :: Web () ()
addQuiz = proc () → do
q ← input ()
ref ← (basil (λx → new ixQuiz x PNil)) q
← addQuestions ref
display (const $ X.toHtml "added quiz with questions.") ()
1
12. Adding a Quiz
Input type
addQuiz :: Web () ()
addQuiz = proc () → do
q ← input ()
ref ← (basil (λx → new ixQuiz x PNil)) q
← addQuestions ref
display (const $ X.toHtml "added quiz with questions.") ()
1
13. Adding a Quiz
Input type Output type
addQuiz :: Web () ()
addQuiz = proc () → do
q ← input ()
ref ← (basil (λx → new ixQuiz x PNil)) q
← addQuestions ref
display (const $ X.toHtml "added quiz with questions.") ()
1
14. Adding a Quiz
Input type Output type Show a form
addQuiz :: Web () ()
addQuiz = proc () → do
q ← input ()
ref ← (basil (λx → new ixQuiz x PNil)) q
← addQuestions ref
display (const $ X.toHtml "added quiz with questions.") ()
1
15. Adding a Quiz
Input type Output type Show a form
addQuiz :: Web () () Store in the database
addQuiz = proc () → do
q ← input ()
ref ← (basil (λx → new ixQuiz x PNil)) q
← addQuestions ref
display (const $ X.toHtml "added quiz with questions.") ()
1
16. Adding a Quiz
Input type Output type Show a form
addQuiz :: Web () () Store in the database
addQuiz = proc () → do
q ← input ()
ref ← (basil (λx → new ixQuiz x PNil)) q
← addQuestions ref
display (const $ X.toHtml "added quiz with questions.") ()
Arrow inputs
1
24. Generating HTML
ghtml :: (Regular a, GHtml (PF a)) ⇒ a → X.Html
data Quiz = Quiz{subject :: String
, description :: String
}
Quiz system
add quiz list quizzes
Quiz
Subject: What programming language are you?
Description: This quiz is about what kind of programming language
you are.
view
take quiz
6
25. Generating HTML
ghtml :: (Regular a, GHtml (PF a)) ⇒ a → X.Html
data Quiz = Quiz{subject :: String
, description :: String
}
Quiz system
add quiz list quizzes
Quiz
Subject: What programming language are you?
Description: This quiz is about what kind of programming language
you are.
view
take quiz
6
27. Generating HTML
Sometimes the generated HTML needs to be
changed Quiz system
add quiz list quizzes
data User = User{name :: String User
, email :: String Name: chris
, password :: String Email: chris@eidhof.nl
} Password: haskell98
Continue
28. Solution: custom HTML
• Error-prone
• What if the input changes?
• What if the HTML library changes?
29. Solution: change datatype
data User = User{name :: String
, email :: String
, password :: NoHtml String
}
• Confuses view and model code
• What if there are multiple views?
30. Solution: change datatype
data User = User{name :: String
, email :: String
, password :: NoHtml String
}
• Confuses view and model code
• What if there are multiple views?
31. Non
Solution: change datatype
data User = User{name :: String
, email :: String
, password :: NoHtml String
}
• Confuses view and model code
• What if there are multiple views?
32. Solution: view datatypes
userToUserView :: User → UserView
userToUserView u = UserView (name u) (email u)
data UserView = UserView{name :: String
, email :: String
}
10
33. Solution: view datatypes
• We can keep using generic programming
• Offer a lot of flexibility for removing,
reordering and displaying of fields
• We still allow manual HTML
35. Generating forms
Sometimes the generated forms need to be
changed
Quiz system
add quiz list quizzes
Name:
Email:
Date:
Submit
36. Generating forms
Sometimes the generated forms need to be
changed
Quiz system
add quiz list quizzes
Name:
Email:
Date:
Submit
Quiz system
add quiz list quizzes
subject:
description:
Larger!
Submit
37. Generating forms
Sometimes the generated forms need to be
changed
Quiz system
Quiz system
add quiz
add quiz list quizzes
list quizzes
Name:
name:
Email:
email:
Date:
Submit
Submit
Quiz system
add quiz list quizzes
subject:
description:
Larger!
Submit
38. Generating forms
Sometimes the generated forms need to be
changed
Quiz system
Quiz system
add quiz
add quiz list quizzes
list quizzes
Name:
name:
Quiz system
Email:
email: add quiz list quizzes
Date:
Submit
subject:
Submit
Quiz system
description:
add quiz list quizzes
subject:
description:
Larger!
Submit
Submit
40. Generic Forms
gform :: (Regular a, GFormlet (PF a))
⇒ Maybe a → Form a
userForm :: Maybe User → Form User
userForm = gform
12
41. User Forms
gform :: (Regular a, GFormlet (PF a))
⇒ Maybe a → Form a
userForm :: Maybe User → Form UserView
userForm = gform ◦ fmap userToUserView
13
42. User Forms
gform :: (Regular a, GFormlet (PF a))
⇒ Maybe a → Form a
userForm :: Maybe User → Form UserView
userForm = gform ◦ fmap userToUserView
How to convert to a
User?
13
43. Solution: lenses
convertUser :: User → UserView
convertUser = Lens (UserView < > name ‘for‘ lName
$
email ‘for‘ lEmail
)
get convertUser :: (User → UserView)
set convertUser :: (UserView → User → User)
13
44. Solution: lenses
convertUser :: User → UserView
convertUser = Lens (UserView < > name ‘for‘ lName
$
email ‘for‘ lEmail
)
Construct a view
get convertUser :: (User → UserView)
set convertUser :: (UserView → User → User)
13
45. Solution: lenses
convertUser :: User → UserView
convertUser = Lens (UserView < > name ‘for‘ lName
$
email ‘for‘ lEmail
)
Construct a view
get convertUser :: (User → UserView)
set convertUser :: (UserView → User → User)
Update original with
values from view
13
56. Solution: entity
relationship models
Attribute
Subject
Name Description
Entity
* 1
Date Response responses Quiz
1
Email
questions
Answers
Relationship
*
ChoiceA Question
ChoiceB ChoiceC Title
57. ER models
• Conceptual model
• Compile to logical models:
• In-memory database
• Relational database
• Diagrams
• ...
58. ER models: constraints
1. Entities should have all the attributes
2. Cardinality is one-to-one, one-to-many,
many-to-one, many-to-many
3. Relationships are only between entities in
the same ER model
4. Relationships should be initialized when
creating a new entity
60. Encoding entities
data Quiz = Quiz{subject :: String
, description :: String
}
Subject
Description
Quiz
1. Entities should have all the attributes
61. Encoding entities
type QuizModel = Quiz :∗: Question :∗: Response :∗: Nil
ixQuiz :: Ix QuizModel Quiz
ixQuestion :: Ix QuizModel Question
ixResponse :: Ix QuizModel Response
17
62. Encoding entities
Type-level list
type QuizModel = Quiz :∗: Question :∗: Response :∗: Nil
ixQuiz :: Ix QuizModel Quiz
ixQuestion :: Ix QuizModel Question
ixResponse :: Ix QuizModel Response
17
63. Encoding entities
Type-level list
type QuizModel = Quiz :∗: Question :∗: Response :∗: Nil
ixQuiz :: Ix QuizModel Quiz
ixQuestion :: Ix QuizModel Question
ixResponse :: Ix QuizModel Response
Typed indexes
17
64. Encoding relationships
2. Cardinality is one-to-one, one-to-many,
many-to-one, many-to-many
3. Relationships are only between entities in
the same ER model
65. A first step
Quiz
data Rel cardL entityL cardR entityR where
Rel :: Rel cardL entitityL cardR entityR
1
questions :: Rel QuizModel One Quiz Many Question questions
questions = Rel
*
Question
18
66. A first step
Cardinality
Quiz
data Rel cardL entityL cardR entityR where
Rel :: Rel cardL entitityL cardR entityR
1
questions :: Rel QuizModel One Quiz Many Question questions
questions = Rel
*
Question
18
67. Constraining cardinality
data One
data Many
data Cardinality a where
One :: Cardinality One
Many :: Cardinality Many
data Rel cardL entityL cardR entityR where
Rel :: Cardinality cardinalityL
→ Cardinality cardinalityR
→ Rel cardinalityL l cardinalityR r
19
68. Constraining cardinality
Type-level
data One
data Many
data Cardinality a where
One :: Cardinality One
Many :: Cardinality Many
data Rel cardL entityL cardR entityR where
Rel :: Cardinality cardinalityL
→ Cardinality cardinalityR
→ Rel cardinalityL l cardinalityR r
19
69. Constraining cardinality
Type-level
data One
data Many
data Cardinality a where Value-level
One :: Cardinality One
Many :: Cardinality Many
data Rel cardL entityL cardR entityR where
Rel :: Cardinality cardinalityL
→ Cardinality cardinalityR
→ Rel cardinalityL l cardinalityR r
19
70. Constraining cardinality
Type-level
data One
data Many
data Cardinality a where Value-level
One :: Cardinality One
Many :: Cardinality Many
data Rel cardL entityL cardR entityR where
Rel :: Cardinality cardinalityL
→ Cardinality cardinalityR
→ Rel cardinalityL l cardinalityR r
2. Cardinality is one-to-one, one-to-many,
many-to-one, many-to-many 19
71. Constraining entities
Quiz
data Rel entities cardinalityL l cardinalityR r where
Rel :: Cardinality cardinalityL
→ Ix entities l 1
→ Cardinality cardinalityR
→ Ix entities r
→ Rel entities cardinalityL l cardinalityR r questions
questions :: Rel QuizModel One Quiz Many Question *
questions = Rel One ixQuiz Many ixQuestion
Question
20
72. Constraining entities
List of all entities (type-level)
Quiz
data Rel entities cardinalityL l cardinalityR r where
Rel :: Cardinality cardinalityL
→ Ix entities l 1
→ Cardinality cardinalityR
→ Ix entities r
→ Rel entities cardinalityL l cardinalityR r questions
questions :: Rel QuizModel One Quiz Many Question *
questions = Rel One ixQuiz Many ixQuestion
Question
20
73. Constraining entities
List of all entities (type-level)
Quiz
data Rel entities cardinalityL l cardinalityR r where
Rel :: Cardinality cardinalityL
→ Ix entities l Indexes into list 1
→ Cardinality cardinalityR
→ Ix entities r
→ Rel entities cardinalityL l cardinalityR r questions
questions :: Rel QuizModel One Quiz Many Question *
questions = Rel One ixQuiz Many ixQuestion
Question
20
74. Constraining entities
List of all entities (type-level)
Quiz
data Rel entities cardinalityL l cardinalityR r where
Rel :: Cardinality cardinalityL
→ Ix entities l Indexes into list 1
→ Cardinality cardinalityR
→ Ix entities r
→ Rel entities cardinalityL l cardinalityR r questions
questions :: Rel QuizModel One Quiz Many Question *
questions = Rel One ixQuiz Many ixQuestion
Question
3. Relationships are only between entities in
the same ER model
20
75. What have we done?
• Described entities
• Described relationships
• Guaranteed 3 out of 4 constraints
79. Adding a Question
Quiz
new :: Ix entities entity
→ entity
→ PList entities entity (InitialValues entities entity rels rels) rels 1
→ Basil entities rels (Ref entities entity)
questions
All to-one relationships involving *
this entity
Question
22
80. InitialValues function
type family InitialValues entities r rels originalRels :: ∗
type instance InitialValues entities r Nil o = Nil
type instance InitialValues entities r (Rel entities c1 from Many to :∗: xs) o =
InitialValues entities r (Rel entities c1 from Many to :∗: xs) o
type instance InitialValues entities r (Rel entities c1 from One to :∗: xs) o =
AppendIfTrue (TypeEq r from)
(InitialValue entities r L (Rel entities c1 from One to) o)
(InitialValues entities r (Rel entities c1 from One to :∗: xs) o)
23
81. InitialValues function
List of all entities (type-level)
type family InitialValues entities r rels originalRels :: ∗
type instance InitialValues entities r Nil o = Nil
type instance InitialValues entities r (Rel entities c1 from Many to :∗: xs) o =
InitialValues entities r (Rel entities c1 from Many to :∗: xs) o
type instance InitialValues entities r (Rel entities c1 from One to :∗: xs) o =
AppendIfTrue (TypeEq r from)
(InitialValue entities r L (Rel entities c1 from One to) o)
(InitialValues entities r (Rel entities c1 from One to :∗: xs) o)
23
82. InitialValues function
List of all entities (type-level) Current entity type
type family InitialValues entities r rels originalRels :: ∗
type instance InitialValues entities r Nil o = Nil
type instance InitialValues entities r (Rel entities c1 from Many to :∗: xs) o =
InitialValues entities r (Rel entities c1 from Many to :∗: xs) o
type instance InitialValues entities r (Rel entities c1 from One to :∗: xs) o =
AppendIfTrue (TypeEq r from)
(InitialValue entities r L (Rel entities c1 from One to) o)
(InitialValues entities r (Rel entities c1 from One to :∗: xs) o)
23
83. InitialValues function
List of all entities (type-level) Current entity type
List of relationships
type family InitialValues entities r rels originalRels :: ∗
type instance InitialValues entities r Nil o = Nil
type instance InitialValues entities r (Rel entities c1 from Many to :∗: xs) o =
InitialValues entities r (Rel entities c1 from Many to :∗: xs) o
type instance InitialValues entities r (Rel entities c1 from One to :∗: xs) o =
AppendIfTrue (TypeEq r from)
(InitialValue entities r L (Rel entities c1 from One to) o)
(InitialValues entities r (Rel entities c1 from One to :∗: xs) o)
23
84. InitialValues function
List of all entities (type-level) Current entity type
List of relationships
type family InitialValues entities r rels originalRels :: ∗
type instance InitialValues entities r Nil o = Nil
type instance InitialValues entities r (Rel entities c1 from Many to :∗: xs) o =
InitialValues entities r (Rel entities c1 from Many to :∗: xs) o
type instance InitialValues entities r (Rel entities c1 from One to :∗: xs) o =
AppendIfTrue (TypeEq r from)
(InitialValue entities r L (Rel entities c1 from One to) o)
(InitialValues entities r (Rel entities c1 from One to :∗: xs) o)
23
85. InitialValues function
List of all entities (type-level) Current entity type
List of relationships
type family InitialValues entities r rels originalRels :: ∗
type instance InitialValues entities r Nil o = Nil
type instance InitialValues entities r (Rel entities c1 from Many to :∗: xs) o =
InitialValues entities r (Rel entities c1 from Many to :∗: xs) o
type instance InitialValues entities r (Rel entities c1 from One to :∗: xs) o =
AppendIfTrue (TypeEq r from)
(InitialValue entities r L (Rel entities c1 from One to) o)
(InitialValues entities r (Rel entities c1 from One to :∗: xs) o)
23
86. InitialValues function
List of all entities (type-level) Current entity type
List of relationships
type family InitialValues entities r rels originalRels :: ∗
type instance InitialValues entities r Nil o = Nil
type instance InitialValues entities r (Rel entities c1 from Many to :∗: xs) o =
InitialValues entities r (Rel entities c1 from Many to :∗: xs) o
type instance InitialValues entities r (Rel entities c1 from One to :∗: xs) o =
AppendIfTrue (TypeEq r from)
(InitialValue entities r L (Rel entities c1 from One to) o)
(InitialValues entities r (Rel entities c1 from One to :∗: xs) o)
23
87. InitialValues function
List of all entities (type-level) Current entity type
List of relationships
type family InitialValues entities r rels originalRels :: ∗
type instance InitialValues entities r Nil o = Nil
type instance InitialValues entities r (Rel entities c1 from Many to :∗: xs) o =
InitialValues entities r (Rel entities c1 from Many to :∗: xs) o
type instance InitialValues entities r (Rel entities c1 from One to :∗: xs) o =
AppendIfTrue (TypeEq r from)
(InitialValue entities r L (Rel entities c1 from One to) o)
(InitialValues entities r (Rel entities c1 from One to :∗: xs) o)
4. Relationships should be initialized when
creating a new entity
23
88. ER model: encoding
entities
data Quiz = Quiz {subject :: String, description :: String}
data Question = Question{title :: String, choiceA, choiceB, choiceC :: String}
data Response = Response{name :: String
, email :: Email
, date :: Date
, answers :: [Answer]
}
data Answer = QA | QB | QC
type QuizModel = Quiz :∗: Question :∗: Response :∗: Nil
28
89. ER model: encoding
relationships
type Questions = Rel QuizModel One Quiz Many Question
type Responses = Rel QuizModel One Quiz Many Response
questions :: Questions
questions = Rel One ixQuiz "quiz" Many ixQuestion "questions"
responses :: Responses
responses = Rel One ixQuiz "quiz" Many ixResponse "responses"
type QuizRelations = Questions :∗: Responses :∗: Nil
29
90. ER model: wrap up
• Enforcing constraints
• In-memory backend
• Relational backend
• Query language
91. ER model: wrap up
• Enforcing constraints
• In-memory backend Same interface!
• Relational backend
• Query language
92. Conclusion
• Three libraries: model, view, controller
• A new start for Haskell web development
• Notably missing: AJAX
93. Query language
data Person = Person{name :: String, age :: Int}
chris = Person "chris" 25
belowDrinkingAge :: Expr Person Bool
belowDrinkingAge = age . < . Constant 18
isChris :: Expr Person Bool
isChris = Not (belowDrinkingAge)
.∧. name . ≡ . Constant "chris"
In-memory database
eval :: Expr entity a → (entity → a)
toSql :: Expr entity a → String
Relational database
25
94. In-memory database
newtype TypeCache a = TypeCache{ cached :: M.Map Int a} deriving Show
type Cache entities = HList (TMap TypeCache entities)
type family RelationStorage rel :: ∗
type instance RelationStorage (Rel entities One r1 One r2) =
M.Map (Ref entities r1) (Ref entities r2)
type instance RelationStorage (Rel entities One r1 Many r2) =
M.Map (Ref entities r2) (Ref entities r1)
type instance RelationStorage (Rel entities Many r1 One r2) =
M.Map (Ref entities r1) (Ref entities r2)
type instance RelationStorage (Rel entities Many r1 Many r2) =
(M.Map (Ref entities r1) (S.Set (Ref entities r2))
, M.Map (Ref entities r2) (S.Set (Ref entities r1)))
26
95. Relational Database
type Table tables atts = HList2 (Attr tables) atts
data TableT a = ∀env schema.TableT
{unTableT :: Table env schema
, trans :: a ⇔ HList schema
}
table :: (Regular a, GName (PF a), GSchema (PF a) schema) ⇒ a → TableT a
class AddRelationship rels tables newTables | rels tables → newTables where
addRelationships :: TList4 Rel rels
→ HList2 TableT tables
→ HList2 TableT newTables
liftOperations :: TList4 Rel rels → Operation tables r → Operation newTables r
27
Hinweis der Redaktion
Ordina uses Haskell to generate PHP + MySQL!
Continuations, views, database
Continuations, views, database
Continuations, views, database
Continuations, views, database
Continuations, views, database
What&#x2019;s the catch: you cannot store the name in the URL
Go through visual syntax: entities, relationships, cardinality
Go through visual syntax: entities, relationships, cardinality
Go through visual syntax: entities, relationships, cardinality