오픈서베이의 새로운 결과 분석 서비스인 오픈애널리틱스를 개발하던 중 발생한 자바 메모리 이슈를 계기로 미시적 관점에서 JVM 메모리 할당을 분석/정리했습니다.
구체적으로, Integer / Long 등의 Object 형 타입과, ArrayList, / LinkedList / Set 등의 자료구조의 메모리 사용을 JDK코드 분석과 각종 도구를 통해 측정하고, 이를 효과적으로 사용하기 위한 방법을 탐구합니다.
오픈서베이의 새로운 결과 분석 서비스인 오픈애널리틱스를 개발하던 중 발생한 자바 메모리 이슈를 계기로 미시적 관점에서 JVM 메모리 할당을 분석/정리했습니다.
구체적으로, Integer / Long 등의 Object 형 타입과, ArrayList, / LinkedList / Set 등의 자료구조의 메모리 사용을 JDK코드 분석과 각종 도구를 통해 측정하고, 이를 효과적으로 사용하기 위한 방법을 탐구합니다.
스트림 API는 람다 표현식을 적극적으로 활용해서 만들어진 자바의 새로운 API 입니다.
스트림 API를 통해서 자바에서도 맵/리듀스 방식으로 대량의 데이터를 순차 또는 병렬 처리를 할 수 있게 되었는데요, 이번 발표를 통해 스트림 API가 무엇인지- 어떻게 써먹는지에 대해 가볍게 씹고 뜯고 맛보고 즐기셨으면 좋겠습니다. :)
예제코드 : https://github.com/arawn/jdk8-stream-api
파이썬 데이터과학 레벨2 - 데이터 시각화와 실전 데이터분석, 그리고 머신러닝 입문 (2020년 이태영)Tae Young Lee
파이썬 데이터과학 레벨2 - 데이터 시각화와 실전 데이터분석, 그리고 머신러닝 입문 (2020년 이태영)
- 코스피 LG유플러스 주가분석, 대한민국 부동산 분석, 강남 아파트 매매 분석, VISA 보고서 분석, 워드클라우드 등
- 국내 어떤 책에서도 다루지 않는 진짜 데이터분석 강의
- (귀차니즘에..) 소수 금융권/대기업/공기업에게만 강의된 자료
LG전자 SEED2016 발표
개미수열 문제를 풀어보면서 다양한 프로그래밍 개념들을 적용시켜봅니다. (Java/JavaScript/Go/C/Scala/Haskell 코드가 조금씩 등장)
- regex
- list processing
- iterator/generator
- coroutine/continuation
- lazy list
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기 DEVIEW 2016Taehoon Kim
발표 영상 : https://goo.gl/jrKrvf
데모 영상 : https://youtu.be/exXD6wJLJ6s
Deep Q-Network, Double Q-learning, Dueling Network 등의 기술을 소개하며, hyperparameter, debugging, ensemble 등의 엔지니어링으로 성능을 끌어 올린 과정을 공유합니다.
스트림 API는 람다 표현식을 적극적으로 활용해서 만들어진 자바의 새로운 API 입니다.
스트림 API를 통해서 자바에서도 맵/리듀스 방식으로 대량의 데이터를 순차 또는 병렬 처리를 할 수 있게 되었는데요, 이번 발표를 통해 스트림 API가 무엇인지- 어떻게 써먹는지에 대해 가볍게 씹고 뜯고 맛보고 즐기셨으면 좋겠습니다. :)
예제코드 : https://github.com/arawn/jdk8-stream-api
파이썬 데이터과학 레벨2 - 데이터 시각화와 실전 데이터분석, 그리고 머신러닝 입문 (2020년 이태영)Tae Young Lee
파이썬 데이터과학 레벨2 - 데이터 시각화와 실전 데이터분석, 그리고 머신러닝 입문 (2020년 이태영)
- 코스피 LG유플러스 주가분석, 대한민국 부동산 분석, 강남 아파트 매매 분석, VISA 보고서 분석, 워드클라우드 등
- 국내 어떤 책에서도 다루지 않는 진짜 데이터분석 강의
- (귀차니즘에..) 소수 금융권/대기업/공기업에게만 강의된 자료
LG전자 SEED2016 발표
개미수열 문제를 풀어보면서 다양한 프로그래밍 개념들을 적용시켜봅니다. (Java/JavaScript/Go/C/Scala/Haskell 코드가 조금씩 등장)
- regex
- list processing
- iterator/generator
- coroutine/continuation
- lazy list
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기 DEVIEW 2016Taehoon Kim
발표 영상 : https://goo.gl/jrKrvf
데모 영상 : https://youtu.be/exXD6wJLJ6s
Deep Q-Network, Double Q-learning, Dueling Network 등의 기술을 소개하며, hyperparameter, debugging, ensemble 등의 엔지니어링으로 성능을 끌어 올린 과정을 공유합니다.
.NET을 처음 접한 프로그래머가 P2P 네트워킹 기능을 구현하면서 마주쳤던 문제와 해결 방법등 개발 경험 전반에 걸쳐서 이야기 해 보려 합니다. 또한 C# 8.0에 추가되는 비동기 스트림을 미리 써볼 수 있는 AsyncEnumerable과 비동기 잠금(lock) 등의 편리한 기능을 갖춘 AsyncEx등의 라이브러리들도 소개합니다.
고급 자바 8 교육 (6일 중 1일차)
티맥스소프트 연구소에 연구소장으로 재직 중이던 2013년 10월에 진행한 자바 언어 강의 내용입니다.
JVM에 대한 이해와 Java 8에 대한 소개를 포함하려고 노력하였습니다.
아래 강의 동영상이 있습니다.
http://javadom.blogspot.com/2017/07/8-6.html
본 자료는 14.12.20 KGUG(Korea Git User Group) 주최 대학생 대상 Git 교육인
“Getting Started with git” 에서 발표된 “Git Basic Commands” 의 발표내용을 담고 있습니다.
본 자료는 크리에이티브 커먼즈 저작자표시-비영리-변경금지(CC BY-NC-ND) 3.0 Unported 라이선스에 따라 이용할 수 있습니다.
본 자료에 사용 된 이미지들은 Creative Common License 를 따르며 이미지 출처는 해당 이미지 하단에 기제 되어 있습니다.
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())로 모델링 한것.
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의 부모클래스