SlideShare a Scribd company logo
1 of 36
Download to read offline
Haxl
한주영
Haxl - 2014년 Open
● Facebook 오픈소스 발표
● ICFP2014 - Siman Marlow
○ 페이퍼(pdf)
○ 동영상
하스켈은 그냥 공부만을 위한 언어였는데, 이건
뭔가 프랙티컬 할 것 같은 느낌
Haxl 공개로 인해 비슷한 구현이 줄줄이...
● Stitch (Twitter)
○ Scala 라이브러리(오픈 소스 아님)
○ Introducing Stitch(YouTube)
● muse
○ Clojure 라이브러리
○ https://github.com/kachayev/muse
● Fetch
○ Scala(.js) 라이브러리
○ http://47deg.github.io/fetch/
● Jobba (Futurice)
○ Scala 라이브러리(오픈 소스 아님)
○ An example of functional design(Blog post)
특정 언어의 라이브러리가 여기 저기
포팅된다는 건 라이브러리 이상의 의미가
있다는 뜻
Haxl?
Haxl is a Haskell library that simplifies access to remote data, such as databases
or web-based services. Haxl can automatically
● batch multiple requests to the same data source,
● request data from multiple data sources concurrently,
● cache previous requests.
… your data-fetching code can be much cleaner and clearer
굉장히 일반적인 문제에 대한
해법. 널리 활용가능할 것 같음
There is no Fork: an Abstraction for Efficient,
Concurrent, and Concise Data Access
Marlow, Simon, et al. "There is no fork: An abstraction for efficient, concurrent, and
concise data access." ACM SIGPLAN Notices. Vol. 49. No. 9. ACM, 2014.
APA
Functional Pearls 같은 페이퍼
● 친절하다.
○ 결과물만 소개하는 대신 라이브러리 설계 과정을 설명해준다!
○ 문제, 핵심 아이디어, 뼈대 코드, 여기에 기능을 하나씩 더해가며 발전시켜 나감
● github.com/facebook/Haxl 의 축약판
○ 페이퍼는 핵심아이디어 위주로 설명
○ 필요하다면 haxl을 직접 볼 수 있다. (아쉽지만 Initial commit이 이미 어느정도 완성형)
○ 실제 코드는 훨씬 복잡 -- 그만큼 현실적
● 만들어진지 얼마되지 않은 라이브러리
○ 군더더기가 적다
● 아무나가 아닌 Simon Marlow
○ 하스켈 공부하다 마주치는 몇명의 Guru들 중 한 사람
○ 특히 Parallel/Concurrent 쪽
https://github.com/simonmar
Key Point
● Implicit concurrency via <*>
f <*> a
● Applicative는 branch를 들여다 볼 수 있음
● f와 a를 모두 들여다보고 Batch/Concurrent fetching을
가능하게 함
● Caching이 가능해졌고, 이에 따라
● consistent 한 결과를 얻을 수 있고
● replay 가능해 진 것은 덤
m >>= f
● m의 결과에 의존적
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
Summary
● Applicative abstraction for implicit concurrency
○ Concurrency monad + Applicative (to introduce concurrency)
● Battery Included (Cache)
○ Performance & Consistent result
● With No Extra Cost
○ mapM = traverse
○ sequence = sequenceA
○ ApplicativeDo
Typical example
do a ← friendsOf x
b ← friendsOf y
return (length (intersect a b))
● friendsOf x와 friendsOf y는 independent ⇒ concurrent
● x,y 에 대해 friendsOf 라는 동일 서비스에 요청 ⇒ batch
● x와 y가 같다면 x에 대해서만 요청 ⇒ cache
Typical example
do a ← friendsOf x
b ← friendsOf y
return (length (intersect a b))
length <$> liftA2 intersect (friendsOf x) (friendsOf y)
ApplicativeDo 확장
GHC 8.0.1에 추가됨
● 원래는 <*>와 ap는 같은 동일하게 동작해야 하지만
● 관찰가능한 차이점이 없기 때문에 <*>를 최적화된 구현으로
동작하도록 변경 =>일종의 Hack이라고 볼 수 있음
Scala와 잠깐 비교
def friendsOf(id: UserId): Future[Set[User]] = …
def numCommonFriends(x: UserId, y: UserId): Future[Int] =
for {
xs <- friendsOf(x)
ys <- friendsOf(y)
} yields (xs & ys).size
Cache는 global/implicit으로 적용 가능
Batching은 어려울 듯...
Rendering a blog
● Types
○ data PostId
○ data Date
○ data PostContent
○ data PostInfo = PostInfo { postId:: PostId, postDate:: Date, postTopic:: String }
● DSL
○ getPostIds :: Fetch [PostId]
○ getPostInfo :: PostId → Fetch PostInfo
○ getPostContent :: PostId → Fetch PostContent
○ getPostViews :: PostId → Fetch Int
blog :: Fetch Html
blog = renderPage <$> leftPane <*> mainPane
mainPane :: Fetch Html
mainPane = do
posts <- getAllPostsInfo :: Fetch [PostInfo]
let ordered = … 최신 글 5개
contents <- mapM (getPostContent . postId) ordered
return $ renderPosts (zip ordered content)
leftPane:: Fetch Html
leftPane = renderSidePane <$> popularPosts <*> topics
data PostInfo = PostInfo {
postId:: PostId,
postDate:: Date,
postTopic:: String }
Concurrency를 직접 사용하지 않는다
그냥 Monad/Applicative/Traversable 일뿐
Quiz
getAllPostsInfo :: Fetch [PostInfo]
getAllPostsInfo = do
ids <- getPostIds
mapM getPostInfo ids
getPostDetails :: PostId -> Fetch (PostInfo, PostContent)
getPostDetails pid = … getPostInfo/getPostContent … 를 어떻게 결합할까?
(,) <$> getPostInfo pid <*> getPostContent pid
직접 Batch를 신경쓰지 않아도 된다
Quiz
쉽게 쌓아올라갈 수 있다.
popularPosts :: Fetch Html
popularPosts = do
pids <- getPostIds
views <- mapM getPostViews pids
let orderd :: [PostId] = … 뷰가 가장 많은 5개 …
contents <- mapM getPostDetails ordered
return (renderPostList contents)
topics :: Fetch Html
topics = do
posts <- getAllPostsInfo
let topicCounts :: Map String Int = … 토픽 별 갯수 …
return (renderTopics topicCounts)
직접 Batch를 신경쓰지 않아도 된다
Quiz
Blog example을 진행하면서
● 동시성을 신경 쓰지 않아도 되고
● Data fetch 순서 신경 쓰지 않아도 되고
○ 다른 언어 환경에서 Future/Promise 쓰는 경우에는 중요한 문제. Modularity를 해친다
● Biz logic에 집중할 수 있었다!
Fetch/Haxl을 구현한 다른 라이브러리는 이런 효과가 조금
떨어진다.
Why?
● Applicative에 implicit하게 녹여낸 것이 특징인데,
● Scala의 경우 명시적으로 사용해야 함
ex) Stitch.traverse(...), Stitch.join(..) 기존의
실제 실행될 때는...
topics, popularPosts, mainPane 세 군데에서 getPostIds로
Block된다.
⇒ 세번 fetch하는대신 한번만 하고, 그 결과 [PostId]를 각각의
Continuation에서 처리한다.
topics와 mainPane은 getPostInfo를 위해 Block되고,
popularPosts는 getPostViews에서 Block된다.
⇒ getPostInfo요청과 getPostViews요청을 나누고 중복제거하여
Concurrent하게 fetch
이 단계에서 topics는 Done, popularPosts와 mainPane은 각각
getPostInfo와 getPostContent에서 Block된다. (blog입장에서는
여전히 Block상태)
⇒ 다시 각각의 묶음으로 Concurrent fetch 진행
만약 Cache가 추가된다면 2단계 mainPane에서 가져온 PostInfo중
3단계 popularPosts에서 필요한 PostInfo와 겹치는 내용이 있으면
추가로 fetch할 필요가 없다.
Fetch 만들기
Fetch a - #1
● Concurrency monad
● Can pause and be resumed (resumption monad)
○ cooperative concurrency ( interleave/roundrobin 등을 구현해볼 수 있음 )
data Fetch a = Done a | Blocked (Fetch a)
계산이 끝났거나 (Done)
뭔가에 의해 Block되었음. Block된 상황이 해결되면 Fetch a로 계속 이어감(continuation)
이 경우, Continuation에서 필요한 데이터를 remote에서 가져와야 하는 것으로 볼 수 있음.
runFetch :: Fetch a -> a
runFetch f = case f of
Done a -> a
Blocked c -> runFetch c
runFetchIO :: Fetch a -> IO a
runFetchIO f = case f of
Done a -> return a
Blocked c -> putStrLn “fetch” >> runFetchIO c
A Poor Man's Concurrency Monad
Fetch a - #2
● Applicative concurrency
● “There is no fork”
data Fetch a = Done a | Blocked (Fetch a)
instance Applicative Fetch where
pure = return
Done g <*> Done y = Done (g y)
Done g <*> Blocked c = Blocked (g <$> c)
Blocked c <*> Done y = Blocked (c <*> Done y)
Blocked c <*> Blocked d = Blocked (c <*> d)
GHC 7.10 Guideline says
fmap = liftM
pure = … define ...
(<*>) = ap
return = pure
(>>=) = … define ...
따로 Applicative를
구현하여 Block된 상황을
모아서 한번에 처리할 수
있도록 함.
Applicative vs Monad
Blocked (Done (+1)) <*> Blocked (Done 1)
⇒ Blocked (Done (+1) <*> Done 1)
⇒ Blocked (Done (1 + 1))
Blocked (Done (+1)) <*> Blocked (Done 1)
⇒ Blocked ((+1) <$> Blocked (Done 1))
⇒ Blocked (Blocked ((+1) <$> Done 1)
⇒ Blocked (Blocked (Done (1 + 1)))
With (<*>) = ap
ap :: (Monad m) => m (a->b) -> m a -> m b
ap m1 m2 = do
x1 <- m1
x2 <- m2
return (x1 x2)
Done f <*> x = f <$> x
Blocked c <*> x = Blocked (c <*> x)
Blocked가 Remote data fetch라면 Monad
`ap`를 이용하는 경우 순차적으로 data fetch가
두 번 발생한다고 볼 수 있다.
Custom applicative instance를 이용하면 이
경우 한 번만 fetch하면 된다.
runFetchIO 를 실행시켜보면 알 수 있음
Fetch a - #3
● Fetching data (Request)
dataFetch :: Request a -> Fetch a
data Fetch a
= Done a
| forall r . Blocked (Request r) (r -> Fetch a)
Blocked 생성자는 Block을 초래한 Request를 포함하고, Continuation은
Request의 결과(r)에 대한 함수 모양으로 바뀌었다.
하지만 multiple request를 batch로 처리할 때 이를 모델링할 수 없다!
결과와 Continuation의 연결을 유지하기 어려움.
r은 결과 값의 타입
Free monad의 liftF와 같음
Fetch a - #4
● Mutable reference holding result
● Enter IO monad
dataFetch :: Request a -> Fetch a
data BlockedRequest = forall a . BlockedRequest (Request a) (IORef (FetchStatus a))
data Result a
= Done a
| Blocked (Seq BlockedRequest) (Fetch a)
newtype Fetch a = Fetch { unFetch :: IO (Result a) } Fetch는 IO를 wrapping
{new/read/write}IORef를 위해 IO가 필요하다.
Continuation으로 직접 넘겨주는 대신
Continuation이 readIORef로 읽어간다.
data FetchStatus a
= NotFetched
| FetchSuccess a
Quiz. Applicative/Monad 구현하기
dataFetch :: Request a → Fetch a
dataFetch request = Fetch $ do
box ← newIORef NotFetched
let br = BlockedRequest request box
let cont = Fetch $ do
FetchSuccess a ← readIORef box
return (Done a)
return (Blocked (singleton br) cont)
IO에서
● fetch결과를 담을 변수 IORef 를 만들고
● 요청과 변수를 binding
● Continuation ‘Fetch’에서는 변수에서
결과를 읽어간다.
● 그럼 writeIORef는 어디서???
fetch :: [BlockedRequest] → IO ()
application-specific data-fetching
concurrency를 직접 사용하고
batch로 이득을 볼 수 있음
fetch가 끝나면 box에는
FetchSuccess가 담겨있어야 한다.
runFetch (Fetch h) = do
r ← h
case r of
Done a → return a
Blocked br cont → do
fetch (toList br)
runFetch cont
runFetch :: Fetch a → IO a
Fetch로 wrapping된 IO를 실행
그 결과가 Done이면 끝
Blocked이면 `fetch`로 데이터를 가져온다음
continuation으로 재귀
list traverse같음
대신, 한 단계 마다 `fetch`로 데이터를 가져와서
다음 단계로 넘겨준다. (side effect)
Fetch a - #5
● Adding a cache, first trial
newtype DataCache = DataCache (forall a. HashMap (Request a) a)
lookup :: Request a → DataCache → Maybe a
lookup key (DataCache m) = Map.lookup key m
insert :: Request a → a → DataCache → DataCache
insert key val (DataCache m) = DataCache $ unsafeCoerce (Map.insert key val m)
The use of unsafe features to implement
a purely functional API is common
practice in Haskell
Request a 에 대해 결과 a 를
저장하는 cache를 만들 수 있다.
그런데 결과만 저장한다면 같은
round에서 발생하는 중복 요청에
대응할 수 없다!
Request a는
Eq/Hashable이어야 함
● Adding a cache, second trial
newtype DataCache = DataCache (forall a. HashMap (Request a) (IORef (FetchStatus a)))
lookup :: Request a → DataCache → Maybe (IORef (FetchStatus a))
insert :: Request a → IORef (FetchStatus a) → DataCache → DataCache
newtype Fetch a = Fetch { unFetch :: IORef DataCache → IO (Result a) }
Fetch의 IO는 Cache를 전달받는다.
State처럼 Cache를 인자로 받고 수정된 Cache를 반환하는 대신,
이번에도 IORef(변수)에 Cache를 저장해두고, 업데이트한다!
State 모나드로 바꿀 수 있을까?
IORef에 FetchStatus를 저장
dataFetch :: Request a → Fetch a
dataFetch req = Fetch $ ref -> do
cache <- readIORef ref
case lookup req cache of
Nothing -> do
… 기존처럼 box 만들고 cache update ...
Just box -> do
r <- readIORef box
case r of
FetchSuccess result -> return (Done result)
NotFetched -> return (Blocked Seq.empty …)
Cache에 FetchStatus가 있나?
No → FetchStatus 추가
Yes → FetchSuccess 인가?
Yes → Done
No → Blocked empty
ref ->
Cache의 유효기간은?
추가 확장
● Exception/Failure
○ data Result a = Done a | Blocked … | Throw SomeException
○ throw :: Exception e => e -> Fetch a
○ catch :: Exception e => Fetch a -> (e -> Fetch a) -> Fetch a
○ data FetchStatus a = NotFetched | FetchSuccess a | FetchFailure SomeException
● Flexibility (Generalize request type)
○ dataFetch :: (DataSource req, Request req a) => req a -> Fetch a
○ class DataSource req where
fetch :: [BlockedRequest req] -> PerformFetch
○ data PerformFetch = SyncFetch (IO ()) | AsyncFetch (IO() -> IO())
○ scheduleFetches :: [PerformFetch] → IO ()
type Request req a =
( Eq (req a)
, Hashable (req a)
, Typeable (req a)
, Show (req a)
, Show a
)
MyRequest a라는 타입은 DataSource class와 연관되어야 하며, fetch는 이제
DataSource class의 메쏘드가 되었다.
scheduleFetches는 각 DataSource별 fetch action(sync/async)을 scheduling
scheduleFetches - 엄청난 한 줄
data PerformFetch = SyncFetch (IO()) | AsyncFetch (IO() → IO())
scheduleFetches :: [PerformFetch] → IO()
scheduleFetches fetches = asyncs syncs
where
asyncs = foldr (.) id [f | AsyncFetch f ← fetches]
syncs = sequence_ [io | SyncFetch io ← fetches]
fetch메쏘드는 `async` 패키지 등을 이용하여 구현할 수 있다.
do a1 <- async (getURL url1)
a2 <- async (getURL url2)
page1 <- wait a1
이 때 wait을 하기 전 뭔가 다른 일을 할 수 있다. 이를 AsyncFetch(IO() →
IO())로 모델링 한것.
Haxl
Fun with Haxl by Simon Marlow
*HaxlBlog> run $ (,) <$> mapM getPostContent [1..3] <*> mapM getPostContent [4..6]
select postid,content from postcontent where postid in (6,5,4,3,2,1)
(["example content 1","example content 2","example content 3"],["example content 4","example content
5","example content 6"])
Haxl/Sqlite 이용하여 간단한 예제를 보여준다.
mapM, <*>로 결합된 계산이 하나의 query로 변환되어 실행된다.
Fun with Haxl by Simon Marlow
type PostId = Int
type PostContent = String
data BlogRequest a where
FetchPosts :: BlogRequest [PostId]
FetchPostContent :: PostId -> BlogRequest PostContent
getPostIds :: GenHaxl u [PostId]
getPostIds = dataFetch FetchPosts
getPostContent :: PostId -> GenHaxl u PostContent
getPostContent = dataFetch . FetchPostContent
instance DataSource u BlogRequest where
fetch (BlogDataState db) _flags _userEnv blockedFetches =
SyncFetch $ batchFetch db blockedFetches
instance StateKey BlogRequest where
data State BlogRequest = BlogRequestState SQLiteHandle
newtype GenHaxl u a -- Functor/Applicative/Monad
dataFetch :: (DataSource u r, Request r a) => r a -> GenHaxl u a
class (DataSourceName req, StateKey req, Show1 req)
=> DataSource u req where
fetch :: State req -> Flags -> u -> [BlockedFetch req] -> PerformFetch
data BlockedFetch r = forall r. BlockedFetch (r a) (ResultVar a)
putSuccess :: ResultVar a -> a -> IO ()
putFailure :: (Exception e) => ResultVar a -> e -> IO ()
data PerformFetch = SyncFetch (IO()) | AsyncFetch (IO() -> IO())
class Typeable f => StateKey (f :: * -> *) where
data State f
runHaxl :: Env u -> GenHaxl u a -> IO a
Conclusion
● Monad로 추상화하기
○ Fetch로 일단 타입 만들고, 여기에 갖가지 기능 덧붙임
● IO 감추기
○ IO/IORef를 사용하되 Fetch타입 바깥으로 드러나지 않도록
○ Clean interface
● 타입 맞춰주기
○ unsafeCoerce :: forall a b. a -> b
● Typeable
○ 동적 타입?
● Free Monad 유행
○ data Free f a = Pure a | Free (f (Free f a))
● 언어확장/런타임확장
○ ApplicativeDo
○ GHC’s runtime에 Unloading기능 추가
● Break the rule
○ Applicative는 Monad의 부모클래스

More Related Content

What's hot

UML distilled 1장 스터디 발표 자료
UML distilled 1장 스터디 발표 자료UML distilled 1장 스터디 발표 자료
UML distilled 1장 스터디 발표 자료beom kyun choi
 
Learning Node Book, Chapter 5
Learning Node Book, Chapter 5Learning Node Book, Chapter 5
Learning Node Book, Chapter 5Ji Hun Kim
 
씹고 뜯고 맛보고 즐기는 스트림 API
씹고 뜯고 맛보고 즐기는 스트림 API씹고 뜯고 맛보고 즐기는 스트림 API
씹고 뜯고 맛보고 즐기는 스트림 APIArawn Park
 
나에 첫번째 자바8 람다식 지앤선
나에 첫번째 자바8 람다식   지앤선나에 첫번째 자바8 람다식   지앤선
나에 첫번째 자바8 람다식 지앤선daewon jeong
 
유니티 REST API를 사용한 파이어 베이스의 데이터 베이스 사용.
유니티 REST API를 사용한 파이어 베이스의 데이터 베이스 사용.유니티 REST API를 사용한 파이어 베이스의 데이터 베이스 사용.
유니티 REST API를 사용한 파이어 베이스의 데이터 베이스 사용.ssuser6dd171
 
Ji 개발 리뷰 (신림프로그래머)
Ji 개발 리뷰 (신림프로그래머)Ji 개발 리뷰 (신림프로그래머)
Ji 개발 리뷰 (신림프로그래머)beom kyun choi
 
Realm은 어떻게 효율적인 데이터베이스를 만들었나?
Realm은 어떻게 효율적인 데이터베이스를 만들었나?Realm은 어떻게 효율적인 데이터베이스를 만들었나?
Realm은 어떻게 효율적인 데이터베이스를 만들었나?Leonardo YongUk Kim
 
Ocaml internal (description of runtime system in Korean)
Ocaml internal (description of runtime system in Korean)Ocaml internal (description of runtime system in Korean)
Ocaml internal (description of runtime system in Korean)Hyungchul Park
 
파이썬 데이터과학 레벨2 - 데이터 시각화와 실전 데이터분석, 그리고 머신러닝 입문 (2020년 이태영)
파이썬 데이터과학 레벨2 - 데이터 시각화와 실전 데이터분석, 그리고 머신러닝 입문 (2020년 이태영)파이썬 데이터과학 레벨2 - 데이터 시각화와 실전 데이터분석, 그리고 머신러닝 입문 (2020년 이태영)
파이썬 데이터과학 레벨2 - 데이터 시각화와 실전 데이터분석, 그리고 머신러닝 입문 (2020년 이태영)Tae Young Lee
 
Javascript개발자의 눈으로 python 들여다보기
Javascript개발자의 눈으로 python 들여다보기Javascript개발자의 눈으로 python 들여다보기
Javascript개발자의 눈으로 python 들여다보기지수 윤
 
일단 시작하는 코틀린
일단 시작하는 코틀린일단 시작하는 코틀린
일단 시작하는 코틀린Park JoongSoo
 

What's hot (12)

UML distilled 1장 스터디 발표 자료
UML distilled 1장 스터디 발표 자료UML distilled 1장 스터디 발표 자료
UML distilled 1장 스터디 발표 자료
 
Learning Node Book, Chapter 5
Learning Node Book, Chapter 5Learning Node Book, Chapter 5
Learning Node Book, Chapter 5
 
씹고 뜯고 맛보고 즐기는 스트림 API
씹고 뜯고 맛보고 즐기는 스트림 API씹고 뜯고 맛보고 즐기는 스트림 API
씹고 뜯고 맛보고 즐기는 스트림 API
 
나에 첫번째 자바8 람다식 지앤선
나에 첫번째 자바8 람다식   지앤선나에 첫번째 자바8 람다식   지앤선
나에 첫번째 자바8 람다식 지앤선
 
유니티 REST API를 사용한 파이어 베이스의 데이터 베이스 사용.
유니티 REST API를 사용한 파이어 베이스의 데이터 베이스 사용.유니티 REST API를 사용한 파이어 베이스의 데이터 베이스 사용.
유니티 REST API를 사용한 파이어 베이스의 데이터 베이스 사용.
 
Ji 개발 리뷰 (신림프로그래머)
Ji 개발 리뷰 (신림프로그래머)Ji 개발 리뷰 (신림프로그래머)
Ji 개발 리뷰 (신림프로그래머)
 
Realm은 어떻게 효율적인 데이터베이스를 만들었나?
Realm은 어떻게 효율적인 데이터베이스를 만들었나?Realm은 어떻게 효율적인 데이터베이스를 만들었나?
Realm은 어떻게 효율적인 데이터베이스를 만들었나?
 
Python basic
Python basicPython basic
Python basic
 
Ocaml internal (description of runtime system in Korean)
Ocaml internal (description of runtime system in Korean)Ocaml internal (description of runtime system in Korean)
Ocaml internal (description of runtime system in Korean)
 
파이썬 데이터과학 레벨2 - 데이터 시각화와 실전 데이터분석, 그리고 머신러닝 입문 (2020년 이태영)
파이썬 데이터과학 레벨2 - 데이터 시각화와 실전 데이터분석, 그리고 머신러닝 입문 (2020년 이태영)파이썬 데이터과학 레벨2 - 데이터 시각화와 실전 데이터분석, 그리고 머신러닝 입문 (2020년 이태영)
파이썬 데이터과학 레벨2 - 데이터 시각화와 실전 데이터분석, 그리고 머신러닝 입문 (2020년 이태영)
 
Javascript개발자의 눈으로 python 들여다보기
Javascript개발자의 눈으로 python 들여다보기Javascript개발자의 눈으로 python 들여다보기
Javascript개발자의 눈으로 python 들여다보기
 
일단 시작하는 코틀린
일단 시작하는 코틀린일단 시작하는 코틀린
일단 시작하는 코틀린
 

Viewers also liked

[SICP] 4.4 Logic Programming : 논리로 프로그램 짜기
[SICP] 4.4 Logic Programming : 논리로 프로그램 짜기[SICP] 4.4 Logic Programming : 논리로 프로그램 짜기
[SICP] 4.4 Logic Programming : 논리로 프로그램 짜기종빈 오
 
Seed2016 - 개미수열 한주영 (annotated)
Seed2016 - 개미수열 한주영 (annotated)Seed2016 - 개미수열 한주영 (annotated)
Seed2016 - 개미수열 한주영 (annotated)Jooyung Han
 
Pure Function and Rx
Pure Function and RxPure Function and Rx
Pure Function and RxHyungho Ko
 
Containerized Data Persistence on Mesos
Containerized Data Persistence on MesosContainerized Data Persistence on Mesos
Containerized Data Persistence on MesosJoe Stein
 
core.logic (Clojure)
core.logic (Clojure)core.logic (Clojure)
core.logic (Clojure)Seonho Kim
 
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기 DEVIEW 2016
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기 DEVIEW 2016딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기 DEVIEW 2016
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기 DEVIEW 2016Taehoon Kim
 
텐서플로우 설치도 했고 튜토리얼도 봤고 기초 예제도 짜봤다면 TensorFlow KR Meetup 2016
텐서플로우 설치도 했고 튜토리얼도 봤고 기초 예제도 짜봤다면 TensorFlow KR Meetup 2016텐서플로우 설치도 했고 튜토리얼도 봤고 기초 예제도 짜봤다면 TensorFlow KR Meetup 2016
텐서플로우 설치도 했고 튜토리얼도 봤고 기초 예제도 짜봤다면 TensorFlow KR Meetup 2016Taehoon Kim
 
지적 대화를 위한 깊고 넓은 딥러닝 PyCon APAC 2016
지적 대화를 위한 깊고 넓은 딥러닝 PyCon APAC 2016지적 대화를 위한 깊고 넓은 딥러닝 PyCon APAC 2016
지적 대화를 위한 깊고 넓은 딥러닝 PyCon APAC 2016Taehoon Kim
 

Viewers also liked (8)

[SICP] 4.4 Logic Programming : 논리로 프로그램 짜기
[SICP] 4.4 Logic Programming : 논리로 프로그램 짜기[SICP] 4.4 Logic Programming : 논리로 프로그램 짜기
[SICP] 4.4 Logic Programming : 논리로 프로그램 짜기
 
Seed2016 - 개미수열 한주영 (annotated)
Seed2016 - 개미수열 한주영 (annotated)Seed2016 - 개미수열 한주영 (annotated)
Seed2016 - 개미수열 한주영 (annotated)
 
Pure Function and Rx
Pure Function and RxPure Function and Rx
Pure Function and Rx
 
Containerized Data Persistence on Mesos
Containerized Data Persistence on MesosContainerized Data Persistence on Mesos
Containerized Data Persistence on Mesos
 
core.logic (Clojure)
core.logic (Clojure)core.logic (Clojure)
core.logic (Clojure)
 
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기 DEVIEW 2016
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기 DEVIEW 2016딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기 DEVIEW 2016
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기 DEVIEW 2016
 
텐서플로우 설치도 했고 튜토리얼도 봤고 기초 예제도 짜봤다면 TensorFlow KR Meetup 2016
텐서플로우 설치도 했고 튜토리얼도 봤고 기초 예제도 짜봤다면 TensorFlow KR Meetup 2016텐서플로우 설치도 했고 튜토리얼도 봤고 기초 예제도 짜봤다면 TensorFlow KR Meetup 2016
텐서플로우 설치도 했고 튜토리얼도 봤고 기초 예제도 짜봤다면 TensorFlow KR Meetup 2016
 
지적 대화를 위한 깊고 넓은 딥러닝 PyCon APAC 2016
지적 대화를 위한 깊고 넓은 딥러닝 PyCon APAC 2016지적 대화를 위한 깊고 넓은 딥러닝 PyCon APAC 2016
지적 대화를 위한 깊고 넓은 딥러닝 PyCon APAC 2016
 

Similar to 하스켈학교 세미나 - Haxl

Undertow RequestBufferingHandler 소개
Undertow RequestBufferingHandler 소개Undertow RequestBufferingHandler 소개
Undertow RequestBufferingHandler 소개Ted Won
 
20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POP20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POPChiwon Song
 
Kth개발자 세미나 1회
Kth개발자 세미나 1회Kth개발자 세미나 1회
Kth개발자 세미나 1회Byeongsu Kang
 
.NET에서 비동기 프로그래밍 배우기
.NET에서 비동기 프로그래밍 배우기.NET에서 비동기 프로그래밍 배우기
.NET에서 비동기 프로그래밍 배우기Seong Won Mun
 
Programming Cascading
Programming CascadingProgramming Cascading
Programming CascadingTaewook Eom
 
[2015-05월 세미나] 파이선 초심자의 Openstack
[2015-05월 세미나] 파이선 초심자의 Openstack[2015-05월 세미나] 파이선 초심자의 Openstack
[2015-05월 세미나] 파이선 초심자의 OpenstackOpenStack Korea Community
 
하스켈 프로그래밍 입문 2
하스켈 프로그래밍 입문 2하스켈 프로그래밍 입문 2
하스켈 프로그래밍 입문 2Kwang Yul Seo
 
Ryu with OpenFlow 1.3, REST API
Ryu with OpenFlow 1.3, REST APIRyu with OpenFlow 1.3, REST API
Ryu with OpenFlow 1.3, REST APIjieun kim
 
20141223 머하웃(mahout) 협업필터링_추천시스템구현
20141223 머하웃(mahout) 협업필터링_추천시스템구현20141223 머하웃(mahout) 협업필터링_추천시스템구현
20141223 머하웃(mahout) 협업필터링_추천시스템구현Tae Young Lee
 
Collection framework
Collection frameworkCollection framework
Collection frameworkssuser34b989
 
[Swift] Data Structure - Graph(DFS)
[Swift] Data Structure - Graph(DFS)[Swift] Data Structure - Graph(DFS)
[Swift] Data Structure - Graph(DFS)Bill Kim
 
Java jungsuk3 ch14_lambda_stream
Java jungsuk3 ch14_lambda_streamJava jungsuk3 ch14_lambda_stream
Java jungsuk3 ch14_lambda_stream성 남궁
 
Laravel 로 배우는 서버사이드 #3
Laravel 로 배우는 서버사이드 #3Laravel 로 배우는 서버사이드 #3
Laravel 로 배우는 서버사이드 #3성일 한
 
Ksug2015 jpa5 스프링과jpa
Ksug2015 jpa5 스프링과jpaKsug2015 jpa5 스프링과jpa
Ksug2015 jpa5 스프링과jpaYounghan Kim
 

Similar to 하스켈학교 세미나 - Haxl (20)

Undertow RequestBufferingHandler 소개
Undertow RequestBufferingHandler 소개Undertow RequestBufferingHandler 소개
Undertow RequestBufferingHandler 소개
 
20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POP20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POP
 
Kth개발자 세미나 1회
Kth개발자 세미나 1회Kth개발자 세미나 1회
Kth개발자 세미나 1회
 
.NET에서 비동기 프로그래밍 배우기
.NET에서 비동기 프로그래밍 배우기.NET에서 비동기 프로그래밍 배우기
.NET에서 비동기 프로그래밍 배우기
 
Programming Cascading
Programming CascadingProgramming Cascading
Programming Cascading
 
[2015-05월 세미나] 파이선 초심자의 Openstack
[2015-05월 세미나] 파이선 초심자의 Openstack[2015-05월 세미나] 파이선 초심자의 Openstack
[2015-05월 세미나] 파이선 초심자의 Openstack
 
Java 8 고급 (1/6)
Java 8 고급 (1/6)Java 8 고급 (1/6)
Java 8 고급 (1/6)
 
Introduce php7
Introduce php7Introduce php7
Introduce php7
 
Valentine
ValentineValentine
Valentine
 
하스켈 프로그래밍 입문 2
하스켈 프로그래밍 입문 2하스켈 프로그래밍 입문 2
하스켈 프로그래밍 입문 2
 
Ryu with OpenFlow 1.3, REST API
Ryu with OpenFlow 1.3, REST APIRyu with OpenFlow 1.3, REST API
Ryu with OpenFlow 1.3, REST API
 
20141223 머하웃(mahout) 협업필터링_추천시스템구현
20141223 머하웃(mahout) 협업필터링_추천시스템구현20141223 머하웃(mahout) 협업필터링_추천시스템구현
20141223 머하웃(mahout) 협업필터링_추천시스템구현
 
Collection framework
Collection frameworkCollection framework
Collection framework
 
Basic git-commands
Basic git-commandsBasic git-commands
Basic git-commands
 
[Swift] Data Structure - Graph(DFS)
[Swift] Data Structure - Graph(DFS)[Swift] Data Structure - Graph(DFS)
[Swift] Data Structure - Graph(DFS)
 
Java jungsuk3 ch14_lambda_stream
Java jungsuk3 ch14_lambda_streamJava jungsuk3 ch14_lambda_stream
Java jungsuk3 ch14_lambda_stream
 
Laravel 로 배우는 서버사이드 #3
Laravel 로 배우는 서버사이드 #3Laravel 로 배우는 서버사이드 #3
Laravel 로 배우는 서버사이드 #3
 
Ksug2015 jpa5 스프링과jpa
Ksug2015 jpa5 스프링과jpaKsug2015 jpa5 스프링과jpa
Ksug2015 jpa5 스프링과jpa
 
pjax
pjaxpjax
pjax
 
자료구조03
자료구조03자료구조03
자료구조03
 

하스켈학교 세미나 - Haxl

  • 2. Haxl - 2014년 Open ● Facebook 오픈소스 발표 ● ICFP2014 - Siman Marlow ○ 페이퍼(pdf) ○ 동영상 하스켈은 그냥 공부만을 위한 언어였는데, 이건 뭔가 프랙티컬 할 것 같은 느낌
  • 3. Haxl 공개로 인해 비슷한 구현이 줄줄이... ● Stitch (Twitter) ○ Scala 라이브러리(오픈 소스 아님) ○ Introducing Stitch(YouTube) ● muse ○ Clojure 라이브러리 ○ https://github.com/kachayev/muse ● Fetch ○ Scala(.js) 라이브러리 ○ http://47deg.github.io/fetch/ ● Jobba (Futurice) ○ Scala 라이브러리(오픈 소스 아님) ○ An example of functional design(Blog post) 특정 언어의 라이브러리가 여기 저기 포팅된다는 건 라이브러리 이상의 의미가 있다는 뜻
  • 4. Haxl? Haxl is a Haskell library that simplifies access to remote data, such as databases or web-based services. Haxl can automatically ● batch multiple requests to the same data source, ● request data from multiple data sources concurrently, ● cache previous requests. … your data-fetching code can be much cleaner and clearer 굉장히 일반적인 문제에 대한 해법. 널리 활용가능할 것 같음
  • 5. There is no Fork: an Abstraction for Efficient, Concurrent, and Concise Data Access Marlow, Simon, et al. "There is no fork: An abstraction for efficient, concurrent, and concise data access." ACM SIGPLAN Notices. Vol. 49. No. 9. ACM, 2014. APA
  • 6. Functional Pearls 같은 페이퍼 ● 친절하다. ○ 결과물만 소개하는 대신 라이브러리 설계 과정을 설명해준다! ○ 문제, 핵심 아이디어, 뼈대 코드, 여기에 기능을 하나씩 더해가며 발전시켜 나감 ● github.com/facebook/Haxl 의 축약판 ○ 페이퍼는 핵심아이디어 위주로 설명 ○ 필요하다면 haxl을 직접 볼 수 있다. (아쉽지만 Initial commit이 이미 어느정도 완성형) ○ 실제 코드는 훨씬 복잡 -- 그만큼 현실적 ● 만들어진지 얼마되지 않은 라이브러리 ○ 군더더기가 적다 ● 아무나가 아닌 Simon Marlow ○ 하스켈 공부하다 마주치는 몇명의 Guru들 중 한 사람 ○ 특히 Parallel/Concurrent 쪽 https://github.com/simonmar
  • 7. Key Point ● Implicit concurrency via <*> f <*> a ● Applicative는 branch를 들여다 볼 수 있음 ● f와 a를 모두 들여다보고 Batch/Concurrent fetching을 가능하게 함 ● Caching이 가능해졌고, 이에 따라 ● consistent 한 결과를 얻을 수 있고 ● replay 가능해 진 것은 덤 m >>= f ● m의 결과에 의존적 class Functor f => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b
  • 8. Summary ● Applicative abstraction for implicit concurrency ○ Concurrency monad + Applicative (to introduce concurrency) ● Battery Included (Cache) ○ Performance & Consistent result ● With No Extra Cost ○ mapM = traverse ○ sequence = sequenceA ○ ApplicativeDo
  • 9. Typical example do a ← friendsOf x b ← friendsOf y return (length (intersect a b)) ● friendsOf x와 friendsOf y는 independent ⇒ concurrent ● x,y 에 대해 friendsOf 라는 동일 서비스에 요청 ⇒ batch ● x와 y가 같다면 x에 대해서만 요청 ⇒ cache
  • 10. Typical example do a ← friendsOf x b ← friendsOf y return (length (intersect a b)) length <$> liftA2 intersect (friendsOf x) (friendsOf y) ApplicativeDo 확장 GHC 8.0.1에 추가됨 ● 원래는 <*>와 ap는 같은 동일하게 동작해야 하지만 ● 관찰가능한 차이점이 없기 때문에 <*>를 최적화된 구현으로 동작하도록 변경 =>일종의 Hack이라고 볼 수 있음
  • 11. Scala와 잠깐 비교 def friendsOf(id: UserId): Future[Set[User]] = … def numCommonFriends(x: UserId, y: UserId): Future[Int] = for { xs <- friendsOf(x) ys <- friendsOf(y) } yields (xs & ys).size Cache는 global/implicit으로 적용 가능 Batching은 어려울 듯...
  • 13. ● Types ○ data PostId ○ data Date ○ data PostContent ○ data PostInfo = PostInfo { postId:: PostId, postDate:: Date, postTopic:: String } ● DSL ○ getPostIds :: Fetch [PostId] ○ getPostInfo :: PostId → Fetch PostInfo ○ getPostContent :: PostId → Fetch PostContent ○ getPostViews :: PostId → Fetch Int
  • 14. blog :: Fetch Html blog = renderPage <$> leftPane <*> mainPane mainPane :: Fetch Html mainPane = do posts <- getAllPostsInfo :: Fetch [PostInfo] let ordered = … 최신 글 5개 contents <- mapM (getPostContent . postId) ordered return $ renderPosts (zip ordered content) leftPane:: Fetch Html leftPane = renderSidePane <$> popularPosts <*> topics data PostInfo = PostInfo { postId:: PostId, postDate:: Date, postTopic:: String } Concurrency를 직접 사용하지 않는다 그냥 Monad/Applicative/Traversable 일뿐 Quiz
  • 15. getAllPostsInfo :: Fetch [PostInfo] getAllPostsInfo = do ids <- getPostIds mapM getPostInfo ids getPostDetails :: PostId -> Fetch (PostInfo, PostContent) getPostDetails pid = … getPostInfo/getPostContent … 를 어떻게 결합할까? (,) <$> getPostInfo pid <*> getPostContent pid 직접 Batch를 신경쓰지 않아도 된다 Quiz 쉽게 쌓아올라갈 수 있다.
  • 16. popularPosts :: Fetch Html popularPosts = do pids <- getPostIds views <- mapM getPostViews pids let orderd :: [PostId] = … 뷰가 가장 많은 5개 … contents <- mapM getPostDetails ordered return (renderPostList contents) topics :: Fetch Html topics = do posts <- getAllPostsInfo let topicCounts :: Map String Int = … 토픽 별 갯수 … return (renderTopics topicCounts) 직접 Batch를 신경쓰지 않아도 된다 Quiz
  • 17. Blog example을 진행하면서 ● 동시성을 신경 쓰지 않아도 되고 ● Data fetch 순서 신경 쓰지 않아도 되고 ○ 다른 언어 환경에서 Future/Promise 쓰는 경우에는 중요한 문제. Modularity를 해친다 ● Biz logic에 집중할 수 있었다! Fetch/Haxl을 구현한 다른 라이브러리는 이런 효과가 조금 떨어진다. Why? ● Applicative에 implicit하게 녹여낸 것이 특징인데, ● Scala의 경우 명시적으로 사용해야 함 ex) Stitch.traverse(...), Stitch.join(..) 기존의
  • 18. 실제 실행될 때는... topics, popularPosts, mainPane 세 군데에서 getPostIds로 Block된다. ⇒ 세번 fetch하는대신 한번만 하고, 그 결과 [PostId]를 각각의 Continuation에서 처리한다. topics와 mainPane은 getPostInfo를 위해 Block되고, popularPosts는 getPostViews에서 Block된다. ⇒ getPostInfo요청과 getPostViews요청을 나누고 중복제거하여 Concurrent하게 fetch 이 단계에서 topics는 Done, popularPosts와 mainPane은 각각 getPostInfo와 getPostContent에서 Block된다. (blog입장에서는 여전히 Block상태) ⇒ 다시 각각의 묶음으로 Concurrent fetch 진행 만약 Cache가 추가된다면 2단계 mainPane에서 가져온 PostInfo중 3단계 popularPosts에서 필요한 PostInfo와 겹치는 내용이 있으면 추가로 fetch할 필요가 없다.
  • 20. Fetch a - #1 ● Concurrency monad ● Can pause and be resumed (resumption monad) ○ cooperative concurrency ( interleave/roundrobin 등을 구현해볼 수 있음 ) data Fetch a = Done a | Blocked (Fetch a) 계산이 끝났거나 (Done) 뭔가에 의해 Block되었음. Block된 상황이 해결되면 Fetch a로 계속 이어감(continuation) 이 경우, Continuation에서 필요한 데이터를 remote에서 가져와야 하는 것으로 볼 수 있음. runFetch :: Fetch a -> a runFetch f = case f of Done a -> a Blocked c -> runFetch c runFetchIO :: Fetch a -> IO a runFetchIO f = case f of Done a -> return a Blocked c -> putStrLn “fetch” >> runFetchIO c
  • 21. A Poor Man's Concurrency Monad Fetch a - #2 ● Applicative concurrency ● “There is no fork” data Fetch a = Done a | Blocked (Fetch a) instance Applicative Fetch where pure = return Done g <*> Done y = Done (g y) Done g <*> Blocked c = Blocked (g <$> c) Blocked c <*> Done y = Blocked (c <*> Done y) Blocked c <*> Blocked d = Blocked (c <*> d) GHC 7.10 Guideline says fmap = liftM pure = … define ... (<*>) = ap return = pure (>>=) = … define ... 따로 Applicative를 구현하여 Block된 상황을 모아서 한번에 처리할 수 있도록 함.
  • 22. Applicative vs Monad Blocked (Done (+1)) <*> Blocked (Done 1) ⇒ Blocked (Done (+1) <*> Done 1) ⇒ Blocked (Done (1 + 1)) Blocked (Done (+1)) <*> Blocked (Done 1) ⇒ Blocked ((+1) <$> Blocked (Done 1)) ⇒ Blocked (Blocked ((+1) <$> Done 1) ⇒ Blocked (Blocked (Done (1 + 1))) With (<*>) = ap ap :: (Monad m) => m (a->b) -> m a -> m b ap m1 m2 = do x1 <- m1 x2 <- m2 return (x1 x2) Done f <*> x = f <$> x Blocked c <*> x = Blocked (c <*> x) Blocked가 Remote data fetch라면 Monad `ap`를 이용하는 경우 순차적으로 data fetch가 두 번 발생한다고 볼 수 있다. Custom applicative instance를 이용하면 이 경우 한 번만 fetch하면 된다. runFetchIO 를 실행시켜보면 알 수 있음
  • 23. Fetch a - #3 ● Fetching data (Request) dataFetch :: Request a -> Fetch a data Fetch a = Done a | forall r . Blocked (Request r) (r -> Fetch a) Blocked 생성자는 Block을 초래한 Request를 포함하고, Continuation은 Request의 결과(r)에 대한 함수 모양으로 바뀌었다. 하지만 multiple request를 batch로 처리할 때 이를 모델링할 수 없다! 결과와 Continuation의 연결을 유지하기 어려움. r은 결과 값의 타입 Free monad의 liftF와 같음
  • 24. Fetch a - #4 ● Mutable reference holding result ● Enter IO monad dataFetch :: Request a -> Fetch a data BlockedRequest = forall a . BlockedRequest (Request a) (IORef (FetchStatus a)) data Result a = Done a | Blocked (Seq BlockedRequest) (Fetch a) newtype Fetch a = Fetch { unFetch :: IO (Result a) } Fetch는 IO를 wrapping {new/read/write}IORef를 위해 IO가 필요하다. Continuation으로 직접 넘겨주는 대신 Continuation이 readIORef로 읽어간다. data FetchStatus a = NotFetched | FetchSuccess a Quiz. Applicative/Monad 구현하기
  • 25. dataFetch :: Request a → Fetch a dataFetch request = Fetch $ do box ← newIORef NotFetched let br = BlockedRequest request box let cont = Fetch $ do FetchSuccess a ← readIORef box return (Done a) return (Blocked (singleton br) cont) IO에서 ● fetch결과를 담을 변수 IORef 를 만들고 ● 요청과 변수를 binding ● Continuation ‘Fetch’에서는 변수에서 결과를 읽어간다. ● 그럼 writeIORef는 어디서??? fetch :: [BlockedRequest] → IO () application-specific data-fetching concurrency를 직접 사용하고 batch로 이득을 볼 수 있음 fetch가 끝나면 box에는 FetchSuccess가 담겨있어야 한다.
  • 26. runFetch (Fetch h) = do r ← h case r of Done a → return a Blocked br cont → do fetch (toList br) runFetch cont runFetch :: Fetch a → IO a Fetch로 wrapping된 IO를 실행 그 결과가 Done이면 끝 Blocked이면 `fetch`로 데이터를 가져온다음 continuation으로 재귀 list traverse같음 대신, 한 단계 마다 `fetch`로 데이터를 가져와서 다음 단계로 넘겨준다. (side effect)
  • 27. Fetch a - #5 ● Adding a cache, first trial newtype DataCache = DataCache (forall a. HashMap (Request a) a) lookup :: Request a → DataCache → Maybe a lookup key (DataCache m) = Map.lookup key m insert :: Request a → a → DataCache → DataCache insert key val (DataCache m) = DataCache $ unsafeCoerce (Map.insert key val m) The use of unsafe features to implement a purely functional API is common practice in Haskell Request a 에 대해 결과 a 를 저장하는 cache를 만들 수 있다. 그런데 결과만 저장한다면 같은 round에서 발생하는 중복 요청에 대응할 수 없다! Request a는 Eq/Hashable이어야 함
  • 28. ● Adding a cache, second trial newtype DataCache = DataCache (forall a. HashMap (Request a) (IORef (FetchStatus a))) lookup :: Request a → DataCache → Maybe (IORef (FetchStatus a)) insert :: Request a → IORef (FetchStatus a) → DataCache → DataCache newtype Fetch a = Fetch { unFetch :: IORef DataCache → IO (Result a) } Fetch의 IO는 Cache를 전달받는다. State처럼 Cache를 인자로 받고 수정된 Cache를 반환하는 대신, 이번에도 IORef(변수)에 Cache를 저장해두고, 업데이트한다! State 모나드로 바꿀 수 있을까? IORef에 FetchStatus를 저장
  • 29. dataFetch :: Request a → Fetch a dataFetch req = Fetch $ ref -> do cache <- readIORef ref case lookup req cache of Nothing -> do … 기존처럼 box 만들고 cache update ... Just box -> do r <- readIORef box case r of FetchSuccess result -> return (Done result) NotFetched -> return (Blocked Seq.empty …) Cache에 FetchStatus가 있나? No → FetchStatus 추가 Yes → FetchSuccess 인가? Yes → Done No → Blocked empty ref -> Cache의 유효기간은?
  • 30. 추가 확장 ● Exception/Failure ○ data Result a = Done a | Blocked … | Throw SomeException ○ throw :: Exception e => e -> Fetch a ○ catch :: Exception e => Fetch a -> (e -> Fetch a) -> Fetch a ○ data FetchStatus a = NotFetched | FetchSuccess a | FetchFailure SomeException ● Flexibility (Generalize request type) ○ dataFetch :: (DataSource req, Request req a) => req a -> Fetch a ○ class DataSource req where fetch :: [BlockedRequest req] -> PerformFetch ○ data PerformFetch = SyncFetch (IO ()) | AsyncFetch (IO() -> IO()) ○ scheduleFetches :: [PerformFetch] → IO () type Request req a = ( Eq (req a) , Hashable (req a) , Typeable (req a) , Show (req a) , Show a ) MyRequest a라는 타입은 DataSource class와 연관되어야 하며, fetch는 이제 DataSource class의 메쏘드가 되었다. scheduleFetches는 각 DataSource별 fetch action(sync/async)을 scheduling
  • 31. scheduleFetches - 엄청난 한 줄 data PerformFetch = SyncFetch (IO()) | AsyncFetch (IO() → IO()) scheduleFetches :: [PerformFetch] → IO() scheduleFetches fetches = asyncs syncs where asyncs = foldr (.) id [f | AsyncFetch f ← fetches] syncs = sequence_ [io | SyncFetch io ← fetches] fetch메쏘드는 `async` 패키지 등을 이용하여 구현할 수 있다. do a1 <- async (getURL url1) a2 <- async (getURL url2) page1 <- wait a1 이 때 wait을 하기 전 뭔가 다른 일을 할 수 있다. 이를 AsyncFetch(IO() → IO())로 모델링 한것.
  • 32. Haxl
  • 33. Fun with Haxl by Simon Marlow *HaxlBlog> run $ (,) <$> mapM getPostContent [1..3] <*> mapM getPostContent [4..6] select postid,content from postcontent where postid in (6,5,4,3,2,1) (["example content 1","example content 2","example content 3"],["example content 4","example content 5","example content 6"]) Haxl/Sqlite 이용하여 간단한 예제를 보여준다. mapM, <*>로 결합된 계산이 하나의 query로 변환되어 실행된다.
  • 34. Fun with Haxl by Simon Marlow type PostId = Int type PostContent = String data BlogRequest a where FetchPosts :: BlogRequest [PostId] FetchPostContent :: PostId -> BlogRequest PostContent getPostIds :: GenHaxl u [PostId] getPostIds = dataFetch FetchPosts getPostContent :: PostId -> GenHaxl u PostContent getPostContent = dataFetch . FetchPostContent instance DataSource u BlogRequest where fetch (BlogDataState db) _flags _userEnv blockedFetches = SyncFetch $ batchFetch db blockedFetches instance StateKey BlogRequest where data State BlogRequest = BlogRequestState SQLiteHandle newtype GenHaxl u a -- Functor/Applicative/Monad dataFetch :: (DataSource u r, Request r a) => r a -> GenHaxl u a class (DataSourceName req, StateKey req, Show1 req) => DataSource u req where fetch :: State req -> Flags -> u -> [BlockedFetch req] -> PerformFetch data BlockedFetch r = forall r. BlockedFetch (r a) (ResultVar a) putSuccess :: ResultVar a -> a -> IO () putFailure :: (Exception e) => ResultVar a -> e -> IO () data PerformFetch = SyncFetch (IO()) | AsyncFetch (IO() -> IO()) class Typeable f => StateKey (f :: * -> *) where data State f runHaxl :: Env u -> GenHaxl u a -> IO a
  • 36. ● Monad로 추상화하기 ○ Fetch로 일단 타입 만들고, 여기에 갖가지 기능 덧붙임 ● IO 감추기 ○ IO/IORef를 사용하되 Fetch타입 바깥으로 드러나지 않도록 ○ Clean interface ● 타입 맞춰주기 ○ unsafeCoerce :: forall a b. a -> b ● Typeable ○ 동적 타입? ● Free Monad 유행 ○ data Free f a = Pure a | Free (f (Free f a)) ● 언어확장/런타임확장 ○ ApplicativeDo ○ GHC’s runtime에 Unloading기능 추가 ● Break the rule ○ Applicative는 Monad의 부모클래스