Skip to content

Instantly share code, notes, and snippets.

View jkarni's full-sized avatar

Julian Arni jkarni

View GitHub Profile
-- first a separate class to interpret the access/permission DSL
class IsAccess a where
getAccess :: Proxy a -> Id -> Bool
-- ^ probably something quite different here. The two main options I see generally
-- are evaluating to some normal form (like AccessType) and not requiring Id, or
-- having Id and then directly evaluating to Bool
-- If you want to be allowed to require other values depending on the type (which I
-- think you do) you'd need an associated type synonym).
instance (IsAccess a, IsAccess b) => IsAccess (a :|| b) where

At first we had "Get a" [note that I'm going to be pretty loose about distinguishing type variables in Haskell and in the metalanguage]. OK, the "Get" part should be static - each endpoint can only "be" one method. The 'a' type can be anything, but in the description (i.e., the API type) it is static and that's fine, since these are algebraic data types, and can themselves represent sums and products of other things (in comparison, the method "place" for the endpoints is just a sum.)

So far we pretended status codes didn't need to be represented, because they could be statically determined by the method type. But of course that's bad HTTP practice - an empty body should probably cause a 204 No Content, for instance. So what we did was defined instances that overlapped on the method argument ("a" in "Get a"). Kind of ugly.

Then came content types. That turned out okay, because generally we're fine considering the acceptable content-types for and endpoint to be static. It would be more general to consider

performRequest' :: Method -> Req -> (Int -> Bool) -> BaseUrl -> EitherT ServantError IO (Int, ByteString, MediaType)
performRequest' reqMethod req isWantedStatus reqHost = do
partialRequest <- liftIO $ reqToRequest req reqHost
let request = partialRequest { Client.method = reqMethod
, checkStatus = \ _status _headers _cookies -> Nothing
}
eResponse <- liftIO $ __withGlobalManager $ \ manager ->
-- completely untested
type MyEndpoint = "something" :> Get Int
class GetMethod a where
getMethod :: Proxy a -> String
instance GetMethod (Get x) where
getMethod _ = "GET"
@jkarni
jkarni / gist:a710bf43931ad3bf6b92
Last active August 29, 2015 14:16
GetWithCookie
data GetWithCookie (a :: *)
type Short a = EitherT (Int,String) IO a
instance HasServer (GetWithCookie a) where
type Server (GetWithCookie a) = EitherT (Int,String) IO (a, ByteString) -- the ByteString is the cookie
route Proxy action request respond = do
e <- runEitherT (action)
respond . succeedWith $ case e of
class PushReaderT a where
type PushReaderSubType a
unPushReaderT :: String -> PushReaderSubType a -> a
instance (PushReaderT b) => PushReaderT (a -> b) where
type PushReaderSubType (a -> b) = a -> PushReaderSubType b
unPushReaderT str f = unPushReaderT str . f
instance (PushReaderT a, PushReaderT b) => PushReaderT (a :<|> b) where
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Typeable
import Data.Data
import Data.Proxy
import GHC.TypeLits
@jkarni
jkarni / HUGSAug2014.md
Last active August 29, 2015 14:05
Polycephaly presentation source.

name: inverse layout: true class: center, middle, inverse

#Polycephaly Or, multiplying instance heads


The Problem


@jkarni
jkarni / pre-push
Last active December 20, 2015 16:09
Keep master safe.
#!/bin/bash
# Prompt before pushing to master. If it's a force push or delete, bail
# Don't be overly reliant on this, though; I'm not sure what the corner cases are.
# As usual, put this is .git/hooks directory, and make it executable with "chmod 755 pre-push"
# Requires git >= 1.8.3
PROT_BRANCH='master'
UNSAFE='force|delete|\-f'
CONFIRM_MSG="Pushing to master - are you absolutely sure you want to continue?"