Mark Seemannã«ããæ°ããèšäºã玹ä»ããŸãã éåžžã«å€ãã®ç¿»èš³ãè¡ãããŠããããã圌ã¯ããã«ã¢ã«ãŠã³ãããªããŠãããã«ãããã®èè
ã«ãªãããã§ãïŒ
æ©èœçã¢ãŒããã¯ãã£ã®è峿·±ãç¹ã¯äœã§ããïŒ ãããããæåã®ç©Žãã«é¥ãåŸåããããŸããéçºè
ã¯ãè¯ãã³ãŒããæžãããšãäœåãªããããç¶æ³ã«é¥ããŸãã
ãªããžã§ã¯ãæåã¢ãŒããã¯ãã£ã«ã€ããŠèª¬æãããšããããŒãããã³ã¢ããã¿ã¢ãŒããã¯ãã£ã®ã¢ã€ãã¢ãæãã€ãããšããããããŸãã ãã€ã³ãã¯ãããžãã¹ããžãã¯ãæè¡çãªå®è£
ã®è©³çްããåé¢ããããããåå¥ã«å€æŽã§ããããã«ããããšã§ãã ããã«ãããããžãã¹ããã¯ãããžãŒã®å€åã«å¯Ÿå¿ããŠæäœã§ããŸãã
ããŒããšã¢ããã¿ãŒ
ããŒããšã¢ããã¿ãŒã®ã¢ãŒããã¯ãã£ãŒã®èåŸã«ããèãæ¹ã¯ãããŒããã¢ããªã±ãŒã·ã§ã³ã®å¢çã衚ãããšã§ãã ããŒãã¯ãå€éšã®äžçãšå¯Ÿè©±ãããã®ã§ãããŠãŒã¶ãŒã€ã³ã¿ãŒãã§ã€ã¹ãã¡ãã»ãŒãžãã¥ãŒãããŒã¿ããŒã¹ããã¡ã€ã«ãã³ãã³ãã©ã€ã³ããã³ãããªã©ã§ãã ããŒãã¯äžçäžã®ã¢ããªã±ãŒã·ã§ã³ã€ã³ã¿ãŒãã§ãŒã¹ã§ãããã¢ããã¿ãŒã¯ããŒããšã¢ããªã±ãŒã·ã§ã³ã¢ãã«éã®å€æãæäŸããŸãã
ã¢ããã¿ã®åœ¹å²ïŒ
èšèšãã¿ãŒã³ãšã㊠ïŒã¯2ã€ã®ç°ãªãã€ã³ã¿ãŒãã§ã€ã¹éã®éä¿¡ãæäŸããããšã§ããããããã¢ããã¿ããšããçšèªãæ£åžžã«éžæãããŸããã
åã«èª¬æããããã«ãInjection Dependencyã䜿çšããŠããå Žåã¯ãããçš®ã®ããŒããšã¢ããã¿ãŒã«é Œãå¿
èŠããããŸãã
ãã ãããã®ã¢ãŒããã¯ãã£ã®åé¡ã¯ããããå®è£
ããã«ã¯å€ãã®èª¬æãå¿
èŠãšæãããããšã§ãã
- Dependency Injectionã«é¢ããç§ã®æ¬ã¯500ããŒãžã®é·ãã§ãã
- ãããŒãã»ããŒãã³ã®ãœãªããååãããã±ãŒãžèšèšãã³ã³ããŒãã³ããªã©ã«é¢ããæ¬ã 700ããŒãžãããããŸãã
- å顿åããã°ã©ãã³ã°-500ããŒãžã
- ãªã©ãªã©...
ç§ã®çµéšã§ã¯ãããŒããšã¢ããã¿ãŒã®ã¢ãŒããã¯ãã£ãŒã®å®è£
ã¯Sisyphusã®åŽåã§ãã ããã«ã¯å€ãã®å€åããå¿
èŠã§ããããã°ããæ°ãæ£ãããšããã«ããŒã¯åã³åããŸãã
ãªããžã§ã¯ãæåããã°ã©ãã³ã°ã§ããŒãããã³ã¢ããã¿ã¢ãŒããã¯ãã£ãå®è£
ããããšã¯å¯èœã§ãããå€å€§ãªåŽåãå¿
èŠã§ãã ããã¯ãšãŠãé£ããã§ããããïŒ
ãã¥ãŒããªã¢ã«ãšããŠã®Haskell
颿°åããã°ã©ãã³ã°ã«çŽç²ã«èå³ãæã£ãŠãHaskellãåŠã¶ããšã«ããŸããã Haskellãå¯äžã®é¢æ°åèšèªã§ãã£ãããã§ã¯ãããŸããããFïŒãClojureãScalaã®ãããã§ãå°éã§ããªãã¬ãã«ã®ã¯ãªãŒã³ããæäŸããŸãã Haskellã§ã¯ã颿°ã¯ããã®åãå¥ã®ããšã瀺ããªãéãçŽç²ã§ãã ããã«ãããèšèšã«æ³šæãæããå¯äœçšã®ãã颿°ããçŽç²ãªé¢æ°ãåé¢ããå¿
èŠããããŸãã
Haskellãç¥ããªãå Žåãå¯äœçšã®ããã³ãŒãã¯IOïŒå
¥å/åºåïŒãšåŒã°ããç¹å®ã®ãã³ã³ããã¹ããå
ã«ãã衚瀺ã§ããŸããã ããã¯ã¢ããåã§ãããããã¯äž»ãªãã®ã§ã¯ãããŸããã äž»ãªããšã¯ã颿°ã®çš®é¡ã«ãã£ãŠããããçŽç²ã§ãããã©ããã倿ã§ããããšã§ãã ã¿ã€ãã®ãã颿°
ReservationRendition -> Either Error Reservation
IO
åã«
IO
ããçŽç²ã§ãã äžæ¹ã次ã®ã¿ã€ãã®é¢æ°ïŒ
ConnectionString -> ZonedTime -> IO Int
è¿ãããåã¯
IO Int
ã§ãããããã¯ãªãŒã³ã§ã¯ãããŸããã ããã¯ãæ»ãå€ãæŽæ°ã§ããããšãæå³ããŸããããã®æŽæ°ã¯é¢æ°åŒã³åºãéã§å€æŽã§ããã³ã³ããã¹ãã«ç±æ¥ããŸãã
Int
ãè¿ã颿°ãš
IO Int
è¿ã颿°ã«ã¯æ ¹æ¬çãªéãããããŸãã Haskellã§ã¯ã
Int
ãè¿ã颿°ã¯
en.wikipedia.org/wiki/Referential_transparencyã«ãªã³ã¯ééçã§ãã ããã¯ã颿°ãåãå
¥åã§åãå€ãè¿ãããšãä¿èšŒãããŠããããšãæå³ããŸãã äžæ¹ã
IO Int
è¿ã颿°ã¯ãã®ãããªä¿èšŒãæäŸããŸããã
Haskellã§ããã°ã©ã ãäœæãããšãã¯ãäžæœãªã³ãŒããã·ã¹ãã ã®å¢çã«ã·ããããããšã«ãããçŽç²ãªé¢æ°ã®æ°ãæå€§åããããã«åªåããå¿
èŠããããŸãã åªããHaskellããã°ã©ã ã«ã¯ãçŽç²ãªé¢æ°ã®å€§èŠæš¡ãªã³ã¢ãšI / Oã·ã§ã«ããããŸãã ããªãã¿ã§ããã
äžè¬çã«ãããã¯Haskellåã·ã¹ãã ãããŒããšã¢ããã¿ãŒã®ã¢ãŒããã¯ãã£ãæäŸããããšãæå³ããŸãã ããŒãã¯å
¥å/åºåã³ãŒãã§ãã ã¢ããªã±ãŒã·ã§ã³ã®äžæ žã¯ããã¹ãŠã®çŽç²ãªæ©èœã§ãã åã·ã¹ãã ã¯èªåçã«ãæåã®ç©Žãã«ããªããæŒã蟌ã¿ãŸãã
Haskellã¯ãçŽç²ãªé¢æ°ãšäžçŽãªé¢æ°ãæç¢ºã«åºå¥ã§ãããããåªããåŠç¿æ¯æŽããŒã«ã§ãã FïŒã³ãŒãããååã«æ©èœããããã©ããããã§ãã¯ããããŒã«ãšããŠäœ¿çšããããšãã§ããŸãã
FïŒã¯äž»ã«é¢æ°åèšèªã§ããããªããžã§ã¯ãæåãŸãã¯åœä»€åã®ã³ãŒããèšè¿°ããããšãã§ããŸãã FïŒã§ãæ©èœçãªãæ¹æ³ã§ã³ãŒããèšè¿°ããã°ãHaskellã«ç°¡åã«å€æã§ããŸãã FïŒã³ãŒããHaskellã«ç¿»èš³ããã®ãé£ããå Žåãããããæ©èœããŠããŸããã
以äžã¯å®éã®äŸã§ãã
FïŒã§ã¢ãŒããŒãåãå
¥ããæåã®è©Šã¿
ç§ã®
Pluralsightã³ãŒã¹ã®FïŒã«ãããã¹ãé§åéçº ïŒçç¥ãããç¡æããŒãžã§ã³ãå©çšå¯èœã§ãïŒ
http :
//www.infoq.com/presentations/mock-fsharp-tdd ïŒãªã³ã©ã€ã³ã¬ã¹ãã©ã³äºçŽã·ã¹ãã ã«HTTP APIãå®è£
ããæ¹æ³ã瀺ããŸãäºçŽãªã¯ãšã¹ããåãå
¥ããŸãã äºçŽãªã¯ãšã¹ããåŠçããéã®æé ã®1ã€ã¯ãã¬ã¹ãã©ã³ã«äºçŽãåãå
¥ããã®ã«ååãªç©ºãåžããããã©ããã確èªããããšã§ãã 颿°ã¯æ¬¡ã®ããã«ãªããŸãã
// int // -> (DateTimeOffset -> int) // -> Reservation // -> Result<Reservation,Error> let check capacity getReservedSeats reservation = let reservedSeats = getReservedSeats reservation.Date if capacity < reservation.Quantity + reservedSeats then Failure CapacityExceeded else Success reservation
解説ã瀺ãããã«ã
getReservedSeats
ã®2çªç®ã®åŒæ°ã¯ã
DateTimeOffset -> int
åã®é¢æ°ã§ãã
check
æ©èœã¯ãããåŒã³åºããŠãèŠæ±ãããæ¥ä»ã®ãã§ã«äºçŽãããŠãã座åžã®æ°ãååŸããŸãã
åäœãã¹ãäžã«ãçŽç²ãªé¢æ°ãã¹ã¿ãã«çœ®ãæããããšãã§ããŸããæ¬¡ã«äŸã瀺ããŸãã
let getReservedSeats _ = 0 let actual = Capacity.check capacity getReservedSeats reservation
ãŸããã¢ããªã±ãŒã·ã§ã³ã®æçµãã«ãäžã«ãåºå®ã®åºå®æ»ãå€ãæã€ã¯ãªãŒã³é¢æ°ã䜿çšãã代ããã«ãããŒã¿ããŒã¹ã«ã¯ãšãªãå®è¡ããŠå¿
èŠãªæ
å ±ãååŸããã¯ãªãŒã³é¢æ°ãäœæã§ããŸãã
let imp = Validate.reservation >> bind (Capacity.check 10 (SqlGateway.getReservedSeats connectionString)) >> map (SqlGateway.saveReservation connectionString)
ããã§ã
SqlGateway.getReservedSeats connectionString
ã¯éšåçã«é©çšããã颿°ã§ããããã®åã¯
DateTimeOffset -> int
ã§ãã FïŒã§ã¯ãã¿ã€ãã«ãã£ãŠãããæ±ããŠãããšèšãããšã¯ã§ããŸããããããã¯ç§ããã®é¢æ°ãæžããããã§ããããšãç¥ã£ãŠããŸãã ãã®é¢æ°ã¯ããŒã¿ããŒã¹ãç
§äŒãããããåç
§ã¯ãªãŒã³ã§ã¯ãããŸããã
ããã¯ãã¹ãŠãFïŒã§ããŸãæ©èœããŸããç¹å®ã®æ©èœãã¯ãªãŒã³ã§ããããã¯ãªãŒã³ã§ãªããã¯ããŠãŒã¶ãŒã«ãã£ãŠç°ãªããŸãã
imp
ã¯ãã®ã¢ããªã±ãŒã·ã§ã³ã®
ã³ã³ããžã·ã§ã³ã«ãŒãã§æ§æãããŠãããããæ±ãã颿°
SqlGateway.getReservedSeats
ããã³
SqlGateway.saveReservation
ã¯ã·ã¹ãã å¢çã«ã®ã¿è¡šç€ºãããŸãã ã·ã¹ãã ã®æ®ãã®éšåã¯ãå¯äœçšããååã«ä¿è·ãããŠããŸãã
æ©èœçã«èŠããŸãããæ¬åœã«ããã§ããïŒ
Haskellãã£ãŒãããã¯
ãã®è³ªåã«çããããã«ãHaskellã§ã¢ããªã±ãŒã·ã§ã³ã®äž»èŠéšåãäœãçŽãããšã«ããŸããã 空åžã確èªããæåã®è©Šã¿ã¯ã次ã®ããã«çŽæ¥ç¿»èš³ãããŸããã
checkCapacity :: Int -> (ZonedTime -> Int) -> Reservation -> Either Error Reservation checkCapacity capacity getReservedSeats reservation = let reservedSeats = getReservedSeats $ date reservation in if capacity < quantity reservation + reservedSeats then Left CapacityExceeded else Right reservation
ããã¯ã³ã³ãã€ã«ãããäžèŠãããšææã«æããŸãã 颿°ã¿ã€ã
getReservedSeats
-
ZonedTime -> Int
IO
ã¯ãã®ã¿ã€ãã®ã©ãã«ãçŸããªããããHaskellã¯ãããã¯ãªãŒã³ã§ããããšãä¿èšŒããŸãã
äžæ¹ãäºçŽæžã¿ã®åº§åžã®æ°ãããŒã¿ããŒã¹ããååŸãã颿°ãå®è£
ããå¿
èŠãããå Žåãæ»ãå€ã倿Žãããå¯èœæ§ãããããããã®æ§è³ªäžãæ±ããŠããå¿
èŠããããŸãã Haskellã§ãããæå¹ã«ããã«ã¯ã颿°ã¯æ¬¡ã®ã¿ã€ãã§ãªããã°ãªããŸããã
getReservedSeatsFromDB :: ConnectionString -> ZonedTime -> IO Int
ConnectionString
ã«æåã®åŒæ°ãéšåçã«é©çšã§ããŸãããæ»ãå€ã¯
IO Int
ã§ã¯ãªã
IO Int
ã«ãªããŸãã
ZonedTime -> IO Int
ãããªé¢æ°ã¯
ZonedTime -> IO Int
ãšåãã§ã¯ãããŸããã IOã³ã³ããã¹ãå
ã§å®è¡ãããå Žåã§ãã
ZonedTime -> IO Int
ã
ZonedTime -> Int
倿ããããšã¯ã§ããŸããã
ãŸãã¯ãIOã³ã³ããã¹ãå
ã§äžçŽãªé¢æ°ãåŒã³åºããIO
Int
ãã
Int
ãæœåºããããšãã§ããŸãã ããã¯
checkCapacity
ã®
checkCapacity
颿°ãšå®å
šã«ã¯äžèŽããªããããèšèšãåèããå¿
èŠããããŸãã FïŒã®ã³ãŒãã¯ãéåžžã«æ©èœçãã«èŠããŸãããããã®èšèšã¯å®éã«ã¯æ©èœçã§ã¯ãªãããšãããããŸããã
checkCapacity
ã®
checkCapacity
颿°ãæ³šææ·±ãèŠããšããªãäºçŽæžã¿ã®å Žæã®æ°ã決å®ããããã«é¢æ°ãæž¡ãå¿
èŠãããã®ãââçåã«æããããããŸããã ãªããã®çªå·ãæž¡ããªãã®ã§ããïŒ
checkCapacity :: Int -> Int -> Reservation -> Either Error Reservation checkCapacity capacity reservedSeats reservation = if capacity < quantity reservation + reservedSeats then Left CapacityExceeded else Right reservation
ãšãŠãç°¡åã§ãã ã·ã¹ãã ã®å¢çã§ã¯ãã¢ããªã±ãŒã·ã§ã³ã¯IOã³ã³ããã¹ãã§å®è¡ãããã¯ãªãŒã³ãªé¢æ°ãšã¢ã³ã¯ãªãŒã³ãªé¢æ°ãäœæã§ããŸãã
import Control.Monad.Trans (liftIO) import Control.Monad.Trans.Either (EitherT(..), hoistEither) postReservation :: ReservationRendition -> IO (HttpResult ()) postReservation candidate = fmap toHttpResult $ runEitherT $ do r <- hoistEither $ validateReservation candidate i <- liftIO $ getReservedSeatsFromDB connStr $ date r hoistEither $ checkCapacity 10 ir >>= liftIO . saveReservation connStr
ïŒå®å
šãªãœãŒã¹ã³ãŒãã¯ãã¡ãããå
¥æã§ããŸãïŒ
https :
//gist.github.com/ploeh/c999e2ae2248bd44d775 ïŒ
ãã®æ§æã®ãã¹ãŠã®è©³çްãçè§£ããŠããªããŠãå¿é
ããªãã§ãã ããã 以äžã®èŠç¹ã«ã€ããŠèª¬æããŸããã
postReservation
颿°ã¯ã
ReservationRendition
ãåãåãïŒãããJSONããã¥ã¡ã³ããšèŠãªããŸãïŒã
IO (HttpResult ())
ãè¿ããŸãã
IO
ã¯ããã®ãã¹ãŠã®æ©èœãIOã¢ããã§å®è¡ãããããšãéç¥ããŸãã èšãæããã°ã颿°ã¯æ±ããŠããŸãã ããã¯ã·ã¹ãã ã®å¢çã§ãããããé©ãããšã§ã¯ãããŸããã
ãŸãã
liftIO
颿°
liftIO
2ååŒã³åºãããããšã«æ³šæããŠãã ããã ãã®æ©èœã詳现ã«çè§£ããå¿
èŠã¯ãããŸããããIOã¿ã€ãããå€ãããã«ãããå¿
èŠããããŸãã ã€ãŸããããšãã°ã
IO Int
ãã
Int
ããã«ããŸãã ãããã£ãŠãã³ãŒããã©ãã§ãããã§ã©ãããããã§ãªãããæããã«ãªããŸã
liftIO
颿°
liftIO
getReservedSeatsFromDB
ãš
saveReservation
é©çšãããŸãã ããã¯ãããã2ã€ã®æ©èœãæ±ããŠããããšã瀺ããŠããŸãã äŸå€ã¡ãœããã§ã¯ãæ®ãã®é¢æ°ïŒ
validateReservation
ã
checkCapacity
ããã³
toHttpResult
ïŒã¯çŽç²ã§ãã
ãŸããçŽç²ãªé¢æ°ãšäžçŽãªé¢æ°ãã©ã®ããã«äº€ä»£ãããããšãã§ããããšããåé¡ãçããŸãã ããèŠããšãããŒã¿ãpure
validateReservation
颿°ãã
getReservedSeatsFromDB
颿°ã«ã©ã®ããã«è»¢éãããŠããããããããäž¡æ¹ã®æ»ãå€ïŒ
r
ããã³
i
ïŒãpure
checkCapacity
颿°ã«æž¡ãããæåŸã«unclean saveReservation颿°ã«æž¡ãããŸãã ããã¯ãã¹ãŠ
(EitherT Error IO) () do
ãããã¯ã§çºçããããããããã®é¢æ°ã®ããããã
Left
è¿ããå Žåã颿°ã¯éããŠæçµãšã©ãŒãçæããŸãã ã©ã¡ããã®ã¿ã€ãã®ã¢ããã®æç¢ºã§èŠèŠçãªç޹ä»ã«ã€ããŠã¯ãScott Wlaschinã®åªããèšäºã§ãã
Railway Oriented Programming ïŒENïŒãåç
§ããŠãã ããã
ãã®åŒã®å€ã¯ãçµã¿èŸŒã¿é¢æ°
runEitherT
ã䜿çšããŠååŸãããŸãã ãããŠåã³ãã®ã¯ãªãŒã³ãªæ©èœã§ïŒ
toHttpResult :: Either Error () -> HttpResult () toHttpResult (Left (ValidationError msg)) = BadRequest msg toHttpResult (Left CapacityExceeded) = StatusCode Forbidden toHttpResult (Right ()) = OK ()
postReservation
颿°å
šäœ
postReservation
æ±ããŠãããIOãåŠçããããã·ã¹ãã ã®ç«¯ã«ãããŸãã åãããšã
getReservedSeatsFromDB
ããã³
saveReservation
ã
saveReservation
ã 以äžã®å³ã®äžéšã«ãããŒã¿ããŒã¹ãæäœããããã®2ã€ã®é¢æ°ãæå³çã«é
眮ããŸãããããã«ããããã«ãã¬ãã«ã®ã¢ãŒããã¯ãã£å³ã«æ
£ããŠããèªè
ã«ã¯éŠŽæã¿ã®ãããã®ã«ãªããŸãã åã®äžã«ãããŒã¿ããŒã¹ã衚ãåç圢ã®ãªããžã§ã¯ãããããšæ³åã§ããŸãã
validateReservation
ããã³
toHttpResult
ã¯ãã¢ããªã±ãŒã·ã§ã³ã¢ãã«ã«å±ãã
toHttpResult
ãšããŠè¡šç€ºã§ããŸãã ãããã¯ã¯ãªãŒã³ã§ãå€éšããŒã¿è¡šçŸãšå
éšããŒã¿è¡šçŸã®éã§å€æãããŸãã æåŸã«ãå¿
èŠã«å¿ããŠã
checkCapacity
颿°ã¯ã¢ããªã±ãŒã·ã§ã³ãã¡ã€ã³ã¢ãã«ã®äžéšã§ãã
F.ã§ã®æåã®è©Šã¿ã®èšèšã®ã»ãšãã©ã¯ã
Capacity.check
颿°ãé€ããŠä¿æãããŸããã Haskellã§ãã¶ã€ã³ãåå®è£
ãããšãFïŒã®ã³ãŒãã«é©çšã§ããéèŠãªæèšãåŸãããŸããã
FïŒã§ã®é§ã®åãåããããã«æ©èœç
å¿
èŠãªå€æŽã¯å°ãããããHaskellã®ã¬ãã¹ã³ã¯FïŒããŒã¹ã®ã³ãŒãã«ç°¡åã«é©çšã§ããŸãã äž»ãªåå ã¯
Capacity.check
颿°ã§ã次ã®ããã«å®è£
ããå¿
èŠããããŸãã
let check capacity reservedSeats reservation = if capacity < reservation.Quantity + reservedSeats then Failure CapacityExceeded else Success reservation
ããã¯å®è£
ãåçŽåããã ãã§ãªããæ§æãããå°ãé
åçã«ããŸãã
let imp = Validate.reservation >> map (fun r -> SqlGateway.getReservedSeats connectionString r.Date, r) >> bind (fun (i, r) -> Capacity.check 10 ir) >> map (SqlGateway.saveReservation connectionString)
ããã¯Haskell颿°ãããå°ãè€éã«èŠããŸãã Haskellã®å©ç¹ã¯ã
do
ãããã¯å
ã§
Monad
ã¯ã©ã¹ãå®è£
ããä»»æã®åãèªåçã«äœ¿çšã§ããããšã§ãã
(EitherT Error IO) ()
ã¯
Monad
ã€ã³ã¹ã¿ã³ã¹ã§ããããã
do
æ§æã¯ç¡æã§ãã
FïŒã§ãåæ§ã®ããšãã§ããŸãããçµæã¿ã€ãã®èšç®åŒã®ç¬èªã®ã³ã³ã¹ãã©ã¯ã¿ãå®è£
ããå¿
èŠããããŸãã
ããã°ã§èª¬æããŸããã
ãŸãšã
åªããæ©èœèšèšã¯ãããŒãããã³ã¢ããã¿ãŒã®ã¢ãŒããã¯ãã£ãŒãšåçã§ãã Haskellããçæ³çãªãæ©èœã¢ãŒããã¯ãã£ã®åºæºãšããŠäœ¿çšãããšãçŽç²ãªé¢æ°ãšäžçŽãªé¢æ°ãæç€ºçã«åºå¥ããããšã§ããããããæåã®ãããããã©ã®ããã«äœæãããããããããŸãã IOã¢ããå
ã§ã¢ããªã±ãŒã·ã§ã³å
šäœãèšè¿°ããªãå ŽåãHaskellã¯èªåçã«éããåæ ããå€éšã®äžçãšã®ãã¹ãŠã®éä¿¡ãã·ã¹ãã ã®å¢çã«ããã·ã¥ããŸãã
FïŒãªã©ã®äžéšã®æ©èœèšèªã¯ããã®åºå¥ãæç€ºçã«äœ¿çšããŸããã ãã ããFïŒã§ã¯ãéå
¬åŒã«å®è£
ããã·ã¹ãã ã®å¢çã«ããäžçŽãªæ©èœãæã€ã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããã®ã¯ç°¡åã§ãã ãã®åºå¥ã¯åã·ã¹ãã ã«ãã£ãŠèª²ããããã®ã§ã¯ãããŸããããããã§ãèªç¶ãªããã§ãã
颿°åããã°ã©ãã³ã°ã®ãããã¯ããããŸã§ä»¥äžã«é¢é£ããŠããå Žåããããã2æ¥éã®11æã®
DotNext 2017ã¢ã¹ã¯ã¯äŒè°ã®ãããã®ã¬ããŒãã«èå³ãããã§ãããã