Part I
Part II
Part III
*Tracker also manages GoodData loads
sauron
mason
circle-docker-haskell
Internal Dependency Graph
// Generate internal dep. graph visual
$ stack dot | dot -Tpng -o sauron.png
// Number of internal packages
$ stack ls dependencies --no-external | wc -l
65
External Dependency Graph
//Generate internal + external dep. graph visual
$ stack dot --no-include-base --external --depth=1 \
| dot -Tpng -o sauron-external.png
// Number of internal + external packages
$ stack ls dependencies --external | wc -l
438
// Lines of Haskell code
sauron hkailahi$ cloc --not-match-d='.*stack.*' --fullpath --include-ext=hs .
3511 text files.
3323 unique files.
3038 files ignored.
github.com/AlDanial/cloc v 1.90 T=1.77 s (466.0 files/s, 54333.7 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Haskell 824 11403 9151 75511
-------------------------------------------------------------------------------
SUM: 824 11403 9151 75511
-------------------------------------------------------------------------------
// Lines of Haskell test code
sauron hkailahi$ find . -name "*Spec.hs" | xargs cloc
212 text files.
212 unique files.
0 files ignored.
github.com/AlDanial/cloc v 1.90 T=0.12 s (1757.6 files/s, 249591.8 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Haskell 212 3414 1192 25500
-------------------------------------------------------------------------------
SUM: 212 3414 1192 25500
-------------------------------------------------------------------------------
// Lines of python, configuration, etc
sauron hkailahi$ cloc --not-match-d='.*stack.*,venv' \
--fullpath --include-ext=py,json,yaml,yml, .
4301 text files.
4009 unique files.
3350 files ignored.
github.com/AlDanial/cloc v 1.90 T=11.02 s (124.3 files/s, 28290.2 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Python 1206 44408 55747 179448
JSON 28 0 0 16462
YAML 136 1444 90 14193
-------------------------------------------------------------------------------
SUM: 1370 45852 55837 210103
-------------------------------------------------------------------------------
Coined by He-Who-Must-Not-Be-Named-Uncle-Bob
### Foobar.py
------------------------------
class Quux(NamedTuple):
id: int
class Foo(NamedTuple):
q: Quux
class Bar(NamedTuple):
q: Quux
*Some horizontal projects, like tracker, are depended on everywhere
Henelis-MacBook-Pro-2:sauron hkailahi$ tree -L 1
.
├── README.md
├── acr-database
├── acr-monitor-core
├── acr-monitor-scripts
├── acrcloud-client
├── acrcloud-core
├── ad-import-etl-core
├── ad-import-etl-scripts
├── ad-metadata-api
├── ad-metadata-database
├── api
├── auth-api
├── build-scripts
├── content-metadata-api
├── content-metadata-database
├── device-config-api
├── device-config-core
├── device-config-handler
├── device-config-manager
├── device-config-metrics
├── device-config-scripts
├── firehose-database
├── gooddata-api
├── gooddata-provisioning-api
├── gooddata-provisioning-core
├── gooddata-provisioning-scripts
├── gtv-scripts
├── hie.yaml
├── import-core
├── in-tab-core
├── in-tab-scripts
├── ingest-api
├── ingest-core
├── ingest-driver
├── ingest-integration
├── ingest-manager
├── ingest-metrics
├── ingest-scripts
├── ingest-settings
├── ingest-tracker
├── ingest-tracker-database
├── kinetiq-api
├── kinetiq-core
├── kinetiq-scripts
├── migrate
├── operations-database
├── operations-database-scripts
├── panel-api
├── pentaho-invoker-core
├── pentaho-invoker-scripts
├── photo-training-api
├── photo-training-handler
├── photo-training-metrics
├── python-scripts
├── ranking-unload-api
├── ranking-unload-core
├── ranking-unload-scripts
├── sauron-external.png
├── sauron.png
├── stack.yaml
├── stack.yaml.lock
├── test-results
├── tivo-etl-api
├── tivo-etl-core
├── tivo-etl-scripts
├── tvision-16.31-201.yaml
├── tvision-shared
├── tvision-shared-example
├── tvision-shared-scripts
├── upload-api
├── upload-core
├── upload-handler
├── upload-manager
├── upload-metrics
├── vidvita-client
├── vidvita-core
├── zoho-api
└── zoho-synch
*No more likely to prevent a logical error
Stackage LTS Releases
TVision Upgrades
cryptonite, file-path-th, HasBigDecimal
interpolator, libssh2
New
-- Sauron diff between lts branch & pre-merged master
$ git diff --shortstat \
478239fe85a97b3df8897516ebe64b6e0a9b0fe9 \
72ee1b74114c051e8226531c47157077d24a4fe3
255 files changed, 1406 insertions(+), 1044 deletions(-)
Notes (Will cover in Part II)
# LTS 16 upgrade
## Breaking Changes
* `MonadFail` Proposal
* Not sure what the monad to throw in? Using `throwIO . userError` as substitute because I’m bad at software
* Try to avoid `MonadFail.fail` at all costs
* Use `parseFail` with `Aeson`, which `= fail` but specific to `FromJSON` implementation which is the point of avoiding `fail`
* `MonadFail` usages (for now)
* `Q` for TH (compile-time error at least, but still gross)
* No `Either String` instance
* https://twitter.com/chris__martin/status/1095839981845790724
* https://twitter.com/taylorfausak/status/1180158792622903298
* `Relude` has instance https://github.com/kowainik/relude/blob/78c307f948c52b0b976fe5a588825a1623d9348a/src/Relude/Monad/Either.hs#L71-L73
* Evidently our datetime parsers were partial
*
* `Path`
* Now need annotation with type in scope now https://github.com/commercialhaskell/path/issues/161#issuecomment-632041470
```
-- |The repository root, as an absolute path.
getRootDir :: Shake.Action FilePath
getRootDir = do
path <- liftIO $ makeAbsolute @(Path Rel Dir) [reldir|.|]
pure $ toFilePath path
```
* `Servant`
* `ServantErr` -> `ServerError`
* `ServantError` -> `ClientError`
* `FailureResponse` now includes the failing request, which is nice. I just dropped it where I saw it but it’s probably useful to log it?
* Swagger
* `& type_ .~` to `& type_ ?~` because optional
* `InsOrdHashSet` on `_swaggerTags`
* `Esqueleto`
* No more `Esqueleto` type, concrete > polymorphic
* `expr` -> `SqlExpr`
* `Esqueleto _ _ backend` -> `SqlBackend`
* `sub_select` x4
* used `subSelectUnsafe` instead of `subSelectMaybe` because I didn’t want to deal with change in type and how to get neighbor combinators working (ex. `Q.not_`)
```
panel-api > /Users/hkailahi/tvision/git/haskell/sauron/panel-api/src/Panel/Database/DeviceReadActions.hs:260:47: error: [-Wdeprecations, -Werror=deprecations]
panel-api > In the use of ‘sub_select’
panel-api > (imported from Database.Esqueleto, but defined in Database.Esqueleto.Internal.Internal):
panel-api > Deprecated: "sub_select
panel-api > sub_select is an unsafe function to use. If used with a SqlQuery that
panel-api > returns 0 results, then it may return NULL despite not mentioning Maybe
panel-api > in the return type. If it returns more than 1 result, then it will throw a
panel-api > SQL error.
panel-api >
panel-api > Instead, consider using one of the following alternatives:
panel-api > - subSelect: attaches a LIMIT 1 and the Maybe return type, totally safe.
panel-api > - subSelectMaybe: Attaches a LIMIT 1, useful for a query that already
panel-api > has a Maybe in the return type.
panel-api > - subSelectCount: Performs a count of the query - this is always safe.
panel-api > - subSelectUnsafe: Performs no checks or guarantees. Safe to use with
panel-api > countRows and friends."
panel-api > |
panel-api > 260 | in Q.case_ [ (Q.exists $ void latestAction, Q.sub_select latestIsAction) ] (Q.val False)
panel-api > |
```
* `Persistent`
* Now requires:
```
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE DerivingStrategies #-}
```
## Extra
* `ClassyPrelude`
* Now exports a lot more
* `time` aka Data.Time.* like `Day`, `isoDefaultLocalTime`, `formatTime`, etc
* Was `formatTime` getting deprecated????
* `Control.Monad.Reader` like `ReaderT`
* `Control.Monad.IO.Class` like `MonadIO`, `liftIO`
* `UnliftIO.Exception` and probably the rest of `unliftio`
* `import UnliftIO.Temporary (withSystemTempDirectory)`
* `UnliftIO.Async`
* `stm` like `TVar`
* `containers` like `Data.HashMap.Lazy`
## Cleanup
* Annotating or inlining `Fail.fail`
* `error "blah"` -> `impureThrow $ flip StringException callStack “blah”`
* These are still partial, but now at least our impure exceptions are intentional and HAVE A CALLSTACK 🎉
## Random
We had redundant `MonadReader`, `MonadCatch`, `HasEnv` constraints on foundational AWS stuff, which for some reason we decided to defer with `{-# OPTIONS_GHC -Wno-redundant-constraints #-}`
## Wat 1 -
## Wat 2 - `device-config-core` missing
```
device-config-handler > <command line>: cannot satisfy -package-id device-config-core-4.0-Hv6wPjw7xB74Wc27BUkz9N device-config-handler > (use -v for more information)
...
...
device-config-scripts > Configuring device-config-scripts-4.0...
device-config-scripts > Cabal-simple_mPHDZzAJ_3.0.1.0_ghc-8.8.4: The following package dependencies
device-config-scripts > were requested
device-config-scripts > --dependency='device-config-core=device-config-core-4.0-Hv6wPjw7xB74Wc27BUkz9N'
device-config-scripts > however the given installed package instance does not exist.
```
## Wat 3 - Test build resource blocking errors?
* `build-lock`
Looks like `HLS` and `mason` are competing (running their own colliding stack processes)
```
Error when running Shake build system:
at action, called at src/Shared/Build/Rules.hs:171:3 in tvision-shared-build-1.0-2Kuf4TFsxabFHFPjQLm3rE:Shared.Build.Rules
at need, called at src/Shared/Build/Rules.hs:174:5 in tvision-shared-build-1.0-2Kuf4TFsxabFHFPjQLm3rE:Shared.Build.Rules
* Depends on: .build/ingest-scripts/doc-test
at need, called at src/Shared/Build/Actions.hs:326:5 in tvision-shared-build-1.0-2Kuf4TFsxabFHFPjQLm3rE:Shared.Build.Actions
* Depends on: .build/ingest-scripts/compile
at cmd_, called at src/Shared/Build/Actions.hs:274:33 in tvision-shared-build-1.0-2Kuf4TFsxabFHFPjQLm3rE:Shared.Build.Actions
* Raised the exception:
Development.Shake.cmd, system command failed
Command line: stack build ingest-scripts --test --no-run-tests --fast
Exit code: 1
Stderr:
ingest-tracker-database> blocking for directory lock on /Users/hkailahi/tvision/git/backend/sauron/ingest-tracker-database/.stack-work/dist/x86_64-osx/Cabal-3.0.1.0/build-lock
ingest-tracker-database> configure (lib)
ingest-tracker-database> Configuring ingest-tracker-database-4.0...
ingest-tracker-database> build (lib)
ingest-tracker-database> Preprocessing library for ingest-tracker-database-4.0..
ingest-tracker-database> Building library for ingest-tracker-database-4.0..
ingest-tracker-database> [ 1 of 13] Compiling Ingest.Tracker.Database [Optimisation flags changed]
ingest-tracker-database> <command line>: dlopen(/Users/hkailahi/tvision/git/backend/sauron/.stack-work/install/x86_64-osx/bd89f4eaefc06cd173fc3ea35b5bcb82189eb2837d9754919e73596a7e16914b/8.8.4/lib/x86_64-osx-ghc-8.8.4/libHSingest-settings-4.0-LNA4Yc0UglxCy6qnNu5PEK-ghc8.8.4.dylib, 5): Symbol not found: _ingestzmapizm4zi0zmCKj364Un0DC2Co9M8Fvp9R_IngestziApiziDriverTypes_zdfFromJSONSparkLocator2_closure
ingest-tracker-database> Referenced from: /Users/hkailahi/tvision/git/backend/sauron/.stack-work/install/x86_64-osx/bd89f4eaefc06cd173fc3ea35b5bcb82189eb2837d9754919e73596a7e16914b/8.8.4/lib/x86_64-osx-ghc-8.8.4/libHSingest-settings-4.0-LNA4Yc0UglxCy6qnNu5PEK-ghc8.8.4.dylib
ingest-tracker-database> Expected in: /Users/hkailahi/tvision/git/backend/sauron/.stack-work/install/x86_64-osx/bd89f4eaefc06cd173fc3ea35b5bcb82189eb2837d9754919e73596a7e16914b/8.8.4/lib/x86_64-osx-ghc-8.8.4/libHSingest-api-4.0-CKj364Un0DC2Co9M8Fvp9R-ghc8.8.4.dylib
ingest-tracker-database> in /Users/hkailahi/tvision/git/backend/sauron/.stack-work/install/x86_64-osx/bd89f4eaefc06cd173fc3ea35b5bcb82189eb2837d9754919e73596a7e16914b/8.8.4/lib/x86_64-osx-ghc-8.8.4/libHSingest-settings-4.0-LNA4Yc0UglxCy6qnNu5PEK-ghc8.8.4.dylib
Progress 1/6
-- While building package ingest-tracker-database-4.0 (scroll up to its section to see the error) using:
/Users/hkailahi/.stack/setup-exe-cache/x86_64-osx/Cabal-simple_mPHDZzAJ_3.0.1.0_ghc-8.8.4 --builddir=.stack-work/dist/x86_64-osx/Cabal-3.0.1.0 build lib:ingest-tracker-database --ghc-options ""
Process exited with code: ExitFailure 1
```
### Wat 4 - IOError != SomeException?
```
kinetiq-core > [ 6 of 10] Compiling Kinetiq.Core.ImportSpec kinetiq-core > kinetiq-core > /Users/hkailahi/tvision/git/backend/sauron/kinetiq-core/test/Kinetiq/Core/ImportSpec.hs:306:15: error: kinetiq-core > • Couldn't match type ‘SomeException’ with ‘IOException’ kinetiq-core > Expected type: IOError -> ConduitT i o m CursorMark kinetiq-core > Actual type: SomeException -> ConduitT i o m CursorMark kinetiq-core > • The first argument of ($) takes one argument, kinetiq-core > its type is ‘cat0 a0 c0’, kinetiq-core > it is specialized to ‘SomeException -> ConduitT i o m CursorMark’ kinetiq-core > In the expression: kinetiq-core > map (const CursorMarkAll) kinetiq-core > . yieldM . throwIO . StreamingErrorServant . ConnectionError kinetiq-core > $ userError "test connection error" kinetiq-core > In an equation for ‘creativeStreamFail’: kinetiq-core > creativeStreamFail kinetiq-core > = map (const CursorMarkAll) kinetiq-core > . yieldM . throwIO . StreamingErrorServant . ConnectionError kinetiq-core > $ userError "test connection error" kinetiq-core > | kinetiq-core > 306 | map (const CursorMarkAll)
```
TODO - Re-enable after re-running shake update-swagger-specs
## Wat 5 - Doc Build Misread Correct Module Name and Failed it as incorrect/mismatching the file location
https://app.circleci.com/pipelines/bitbucket/TVision-Insights/sauron/4889/workflows/9eeadb2a-9adc-4707-8721-996f58fcfdf1/jobs/5260/parallel-runs/2?filterBy=FAILED
```
auth-api >
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > src/Auth/Api/Database/Model.hs:1:1: error:
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > File name does not match module name:
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > Saw: ‘Main’
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > Expected: ‘Auth.Api.Database.Model’
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > |
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > 1 | {-# LANGUAGE UndecidableInstances #-}
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > | ^
```
But file clearly uses `Auth.Api.Database.Model`
```
{-# LANGUAGE UndecidableInstances #-}
-- ^Required for using `persistent`
module Auth.Api.Database.Model where
...
```
This isn’t an issue in other places? There’s an issue that says this the error when the module is unparseable by haddock, though I’m not sure what’s in the way https://github.com/commercialhaskell/stack/issues/1549
Probably the comment underneath it not being `module`?
Including LTS 16 (GHC 8.8) Upgrade Notes
A 5 Minute Introduction
Left: https://serokell.medium.com/haskell-history-of-a-community-powered-language-b720ff6b54d
Right: https://www.futurelearn.com/info/courses/functional-programming-haskell/0/steps/27218
(Not Introductory)
Relevant to MonadFail and QualifiedDo discussions
Monads give Haskell programmers an interface for combining computations under some shared context
This context is given as a type, and it's behavior is specified by that type's monad implementation
instance Monad Maybe ...
instance Monad (Either e) ...
instance Monad IO ...
instance Monad Reader ...
instance Monad State ...
Not necessary* to use monadic interface
Language provides a familiar, imperative looking syntax sugar that is nice to use and understand.
# Without Syntax
aShortCircuitingFn :: Either SomeError SomeResult
aShortCircuitingFn =
case doThingOrFail of
Left err1 -> Left err1
Right result1 ->
case doThingWithResult1OrFail result1 of
Left err2 -> Left err2
Right result2 -> Right result2
* IO doesn't export constructor, but does have Monad instance
Do-syntax
a familiar, imperative looking syntax sugar for Monads
# Monad Do Syntax
aShortCircuitingFn :: Either SomeError SomeResult
aShortCircuitingFn = Either.do
result1 <- doThingOrFail
result2 <- doThingWithResult1OrFail result1
pure result2
someShortCircuitingFn :: Either SomeError SomeResult
someShortCircuitingFn = Either.do
result1 <- doThingOrFail
result2 <- doThingWithResult1OrFail result1
pure result2
someAsyncComputation :: Async ()
someAsyncComputation = Async.do
response1 <- makeAsyncRequest1
response2 <- makeAsyncRequest2
doSomething response1
doSomething response2
printThings :: IO ()
printThings = IO.do
input <- getLine
print input
Common interface for asynchronous, short-circuiting, stateful, non-deterministic, effectful, linear, and/or other computations
Functor Applicative Monad hierarchy
Every Monad is an Applicative and every Applicative is a Functor.
Applicatives are weaker than monads, and can be used to sequence independent computations
Can't chain dependent ones as with Monads
Functors one the other hand can apply one or several transformations to one computation
Can’t sequence multiple computations
class Functor f where ...
class (Functor f) => Applicative f where ...
class (Applicative f) => Monad f where ...
Haskell-Specific Code Changes
Notes (Will cover in Part II)
# LTS 16 upgrade
## Breaking Changes
* `MonadFail` Proposal
* Not sure what the monad to throw in? Using `throwIO . userError` as substitute because I’m bad at software
* Try to avoid `MonadFail.fail` at all costs
* Use `parseFail` with `Aeson`, which `= fail` but specific to `FromJSON` implementation which is the point of avoiding `fail`
* `MonadFail` usages (for now)
* `Q` for TH (compile-time error at least, but still gross)
* No `Either String` instance
* https://twitter.com/chris__martin/status/1095839981845790724
* https://twitter.com/taylorfausak/status/1180158792622903298
* `Relude` has instance https://github.com/kowainik/relude/blob/78c307f948c52b0b976fe5a588825a1623d9348a/src/Relude/Monad/Either.hs#L71-L73
* Evidently our datetime parsers were partial
*
* `Path`
* Now need annotation with type in scope now https://github.com/commercialhaskell/path/issues/161#issuecomment-632041470
```
-- |The repository root, as an absolute path.
getRootDir :: Shake.Action FilePath
getRootDir = do
path <- liftIO $ makeAbsolute @(Path Rel Dir) [reldir|.|]
pure $ toFilePath path
```
* `Servant`
* `ServantErr` -> `ServerError`
* `ServantError` -> `ClientError`
* `FailureResponse` now includes the failing request, which is nice. I just dropped it where I saw it but it’s probably useful to log it?
* Swagger
* `& type_ .~` to `& type_ ?~` because optional
* `InsOrdHashSet` on `_swaggerTags`
* `Esqueleto`
* No more `Esqueleto` type, concrete > polymorphic
* `expr` -> `SqlExpr`
* `Esqueleto _ _ backend` -> `SqlBackend`
* `sub_select` x4
* used `subSelectUnsafe` instead of `subSelectMaybe` because I didn’t want to deal with change in type and how to get neighbor combinators working (ex. `Q.not_`)
```
panel-api > /Users/hkailahi/tvision/git/haskell/sauron/panel-api/src/Panel/Database/DeviceReadActions.hs:260:47: error: [-Wdeprecations, -Werror=deprecations]
panel-api > In the use of ‘sub_select’
panel-api > (imported from Database.Esqueleto, but defined in Database.Esqueleto.Internal.Internal):
panel-api > Deprecated: "sub_select
panel-api > sub_select is an unsafe function to use. If used with a SqlQuery that
panel-api > returns 0 results, then it may return NULL despite not mentioning Maybe
panel-api > in the return type. If it returns more than 1 result, then it will throw a
panel-api > SQL error.
panel-api >
panel-api > Instead, consider using one of the following alternatives:
panel-api > - subSelect: attaches a LIMIT 1 and the Maybe return type, totally safe.
panel-api > - subSelectMaybe: Attaches a LIMIT 1, useful for a query that already
panel-api > has a Maybe in the return type.
panel-api > - subSelectCount: Performs a count of the query - this is always safe.
panel-api > - subSelectUnsafe: Performs no checks or guarantees. Safe to use with
panel-api > countRows and friends."
panel-api > |
panel-api > 260 | in Q.case_ [ (Q.exists $ void latestAction, Q.sub_select latestIsAction) ] (Q.val False)
panel-api > |
```
* `Persistent`
* Now requires:
```
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE DerivingStrategies #-}
```
## Extra
* `ClassyPrelude`
* Now exports a lot more
* `time` aka Data.Time.* like `Day`, `isoDefaultLocalTime`, `formatTime`, etc
* Was `formatTime` getting deprecated????
* `Control.Monad.Reader` like `ReaderT`
* `Control.Monad.IO.Class` like `MonadIO`, `liftIO`
* `UnliftIO.Exception` and probably the rest of `unliftio`
* `import UnliftIO.Temporary (withSystemTempDirectory)`
* `UnliftIO.Async`
* `stm` like `TVar`
* `containers` like `Data.HashMap.Lazy`
## Cleanup
* Annotating or inlining `Fail.fail`
* `error "blah"` -> `impureThrow $ flip StringException callStack “blah”`
* These are still partial, but now at least our impure exceptions are intentional and HAVE A CALLSTACK 🎉
## Random
We had redundant `MonadReader`, `MonadCatch`, `HasEnv` constraints on foundational AWS stuff, which for some reason we decided to defer with `{-# OPTIONS_GHC -Wno-redundant-constraints #-}`
## Wat 1 -
## Wat 2 - `device-config-core` missing
```
device-config-handler > <command line>: cannot satisfy -package-id device-config-core-4.0-Hv6wPjw7xB74Wc27BUkz9N device-config-handler > (use -v for more information)
...
...
device-config-scripts > Configuring device-config-scripts-4.0...
device-config-scripts > Cabal-simple_mPHDZzAJ_3.0.1.0_ghc-8.8.4: The following package dependencies
device-config-scripts > were requested
device-config-scripts > --dependency='device-config-core=device-config-core-4.0-Hv6wPjw7xB74Wc27BUkz9N'
device-config-scripts > however the given installed package instance does not exist.
```
## Wat 3 - Test build resource blocking errors?
* `build-lock`
Looks like `HLS` and `mason` are competing (running their own colliding stack processes)
```
Error when running Shake build system:
at action, called at src/Shared/Build/Rules.hs:171:3 in tvision-shared-build-1.0-2Kuf4TFsxabFHFPjQLm3rE:Shared.Build.Rules
at need, called at src/Shared/Build/Rules.hs:174:5 in tvision-shared-build-1.0-2Kuf4TFsxabFHFPjQLm3rE:Shared.Build.Rules
* Depends on: .build/ingest-scripts/doc-test
at need, called at src/Shared/Build/Actions.hs:326:5 in tvision-shared-build-1.0-2Kuf4TFsxabFHFPjQLm3rE:Shared.Build.Actions
* Depends on: .build/ingest-scripts/compile
at cmd_, called at src/Shared/Build/Actions.hs:274:33 in tvision-shared-build-1.0-2Kuf4TFsxabFHFPjQLm3rE:Shared.Build.Actions
* Raised the exception:
Development.Shake.cmd, system command failed
Command line: stack build ingest-scripts --test --no-run-tests --fast
Exit code: 1
Stderr:
ingest-tracker-database> blocking for directory lock on /Users/hkailahi/tvision/git/backend/sauron/ingest-tracker-database/.stack-work/dist/x86_64-osx/Cabal-3.0.1.0/build-lock
ingest-tracker-database> configure (lib)
ingest-tracker-database> Configuring ingest-tracker-database-4.0...
ingest-tracker-database> build (lib)
ingest-tracker-database> Preprocessing library for ingest-tracker-database-4.0..
ingest-tracker-database> Building library for ingest-tracker-database-4.0..
ingest-tracker-database> [ 1 of 13] Compiling Ingest.Tracker.Database [Optimisation flags changed]
ingest-tracker-database> <command line>: dlopen(/Users/hkailahi/tvision/git/backend/sauron/.stack-work/install/x86_64-osx/bd89f4eaefc06cd173fc3ea35b5bcb82189eb2837d9754919e73596a7e16914b/8.8.4/lib/x86_64-osx-ghc-8.8.4/libHSingest-settings-4.0-LNA4Yc0UglxCy6qnNu5PEK-ghc8.8.4.dylib, 5): Symbol not found: _ingestzmapizm4zi0zmCKj364Un0DC2Co9M8Fvp9R_IngestziApiziDriverTypes_zdfFromJSONSparkLocator2_closure
ingest-tracker-database> Referenced from: /Users/hkailahi/tvision/git/backend/sauron/.stack-work/install/x86_64-osx/bd89f4eaefc06cd173fc3ea35b5bcb82189eb2837d9754919e73596a7e16914b/8.8.4/lib/x86_64-osx-ghc-8.8.4/libHSingest-settings-4.0-LNA4Yc0UglxCy6qnNu5PEK-ghc8.8.4.dylib
ingest-tracker-database> Expected in: /Users/hkailahi/tvision/git/backend/sauron/.stack-work/install/x86_64-osx/bd89f4eaefc06cd173fc3ea35b5bcb82189eb2837d9754919e73596a7e16914b/8.8.4/lib/x86_64-osx-ghc-8.8.4/libHSingest-api-4.0-CKj364Un0DC2Co9M8Fvp9R-ghc8.8.4.dylib
ingest-tracker-database> in /Users/hkailahi/tvision/git/backend/sauron/.stack-work/install/x86_64-osx/bd89f4eaefc06cd173fc3ea35b5bcb82189eb2837d9754919e73596a7e16914b/8.8.4/lib/x86_64-osx-ghc-8.8.4/libHSingest-settings-4.0-LNA4Yc0UglxCy6qnNu5PEK-ghc8.8.4.dylib
Progress 1/6
-- While building package ingest-tracker-database-4.0 (scroll up to its section to see the error) using:
/Users/hkailahi/.stack/setup-exe-cache/x86_64-osx/Cabal-simple_mPHDZzAJ_3.0.1.0_ghc-8.8.4 --builddir=.stack-work/dist/x86_64-osx/Cabal-3.0.1.0 build lib:ingest-tracker-database --ghc-options ""
Process exited with code: ExitFailure 1
```
### Wat 4 - IOError != SomeException?
```
kinetiq-core > [ 6 of 10] Compiling Kinetiq.Core.ImportSpec kinetiq-core > kinetiq-core > /Users/hkailahi/tvision/git/backend/sauron/kinetiq-core/test/Kinetiq/Core/ImportSpec.hs:306:15: error: kinetiq-core > • Couldn't match type ‘SomeException’ with ‘IOException’ kinetiq-core > Expected type: IOError -> ConduitT i o m CursorMark kinetiq-core > Actual type: SomeException -> ConduitT i o m CursorMark kinetiq-core > • The first argument of ($) takes one argument, kinetiq-core > its type is ‘cat0 a0 c0’, kinetiq-core > it is specialized to ‘SomeException -> ConduitT i o m CursorMark’ kinetiq-core > In the expression: kinetiq-core > map (const CursorMarkAll) kinetiq-core > . yieldM . throwIO . StreamingErrorServant . ConnectionError kinetiq-core > $ userError "test connection error" kinetiq-core > In an equation for ‘creativeStreamFail’: kinetiq-core > creativeStreamFail kinetiq-core > = map (const CursorMarkAll) kinetiq-core > . yieldM . throwIO . StreamingErrorServant . ConnectionError kinetiq-core > $ userError "test connection error" kinetiq-core > | kinetiq-core > 306 | map (const CursorMarkAll)
```
TODO - Re-enable after re-running shake update-swagger-specs
## Wat 5 - Doc Build Misread Correct Module Name and Failed it as incorrect/mismatching the file location
https://app.circleci.com/pipelines/bitbucket/TVision-Insights/sauron/4889/workflows/9eeadb2a-9adc-4707-8721-996f58fcfdf1/jobs/5260/parallel-runs/2?filterBy=FAILED
```
auth-api >
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > src/Auth/Api/Database/Model.hs:1:1: error:
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > File name does not match module name:
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > Saw: ‘Main’
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > Expected: ‘Auth.Api.Database.Model’
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > |
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > 1 | {-# LANGUAGE UndecidableInstances #-}
Progress 11/65: auth-api, kinetiq-api, panel-api, vidvita-client auth-api > | ^
```
But file clearly uses `Auth.Api.Database.Model`
```
{-# LANGUAGE UndecidableInstances #-}
-- ^Required for using `persistent`
module Auth.Api.Database.Model where
...
```
This isn’t an issue in other places? There’s an issue that says this the error when the module is unparseable by haddock, though I’m not sure what’s in the way https://github.com/commercialhaskell/stack/issues/1549
Probably the comment underneath it not being `module`?
-- Default instances
instance MonadFail Maybe where
fail _ = Nothing
instance MonadFail [] where
fail _ = []
instance MonadFail IO where
fail = failIO -- basically `throwString`
$ count_prev_occs 'fail [\.|\$|\"]'
237
Server (servant-server)
ServantErr -> ServerError
Client (servant-client)
ServantError -> ClientError
ClientError.FailureResponse now includes the failing request, which is nice
I just dropped the field where it came up but it’s would be useful to log it
$ count_prev_occs 'ServantErr'
98
$ count_prev_occs 'FailureResponse'
19
$ count_prev_occs "[-|=]> \(expr\|query\)"
53
time
Day, isoDefaultLocalTime, formatTime
transformers
ReaderT, MonadIO (liftIO)
unliftio
UnliftIO.Exception, UnliftIO.Temporary (withSystemTempDirectory), UnliftIO.Async
stm
TVar
containers
Data.HashMap.Lazy
more...
$ export PREV_REV=478239fe85a97b3df8897516ebe64b6e0a9b0fe9;
$ pickndiff_file() {
> export FILE=$(git grep "$1" $PREV_REV | fzf | cut -f 2 -d :)
> if [ -z $FILE ]
> then echo "No file choosen"
> else
> echo "Diffing $PREV_REV:$FILE..."
> git diff $PREV_REV:$FILE master:$FILE | delta
> fi
> }
$ count_prev_occs() { git grep "$1" $PREV_REV | wc -l; }
error "blah" → impureThrow $ flip StringException callStack “blah”
These are still partial, but now at least our impure exceptions are intentional and have a stack trace 🎉
Foundational AWS streaming utilities had redundant MonadReader, MonadCatch, Aws.HasEnv constraints
GHC 8.6.1 Release Announcement
Reuse typeclass instances from other types
with the same shape
{-# LANGUAGE DerivingVia #-}
newtype VarChar255 = VarChar Text
deriving (Eq, Show)
deriving (SqlField) via SqlText
deriving (Arbitrary) via UTF8UpToNCharacters 255
newtype RowCount = RowCount Int
deriving (Eq, Show, Num)
deriving (Semigroup, Monoid) via Sum Int
Moar type level prolog
{-# LANGUAGE QuantifiedConstraints #-}
class (forall a. Functor (p a)) => Bifunctor p where...
class (forall a c. Functor (p a)) => Profunctor p where...
class (forall m. Monad m => Monad (t m))
=> MonadTrans t where
lift :: m a -> t m a
class Bifunctor p where...
class Profunctor p where...
class MonadTrans t where
lift :: (Monad m) => m a -> t m a
Moar type level prolog
{-# LANGUAGE QuantifiedConstraints #-}
class (forall a. Functor (p a)) => Bifunctor p where...
class (forall a c. Functor (p a)) => Profunctor p where...
class (forall m. Monad m => Monad (t m))
=> MonadTrans t where
lift :: m a -> t m a
class Bifunctor p where...
class Profunctor p where...
class MonadTrans t where
lift :: (Monad m) => m a -> t m a
Moar type level prolog
{-# LANGUAGE QuantifiedConstraints #-}
class (forall a. Functor (p a)) => Bifunctor p where...
class (forall a c. Functor (p a)) => Profunctor p where...
class (forall m. Monad m => Monad (t m))
=> MonadTrans t where
lift :: m a -> t m a
class Bifunctor p where...
class Profunctor p where...
class MonadTrans t where
lift :: (Monad m) => m a -> t m a
Get info on type/function in the REPL
$ ghci
...
λ> :doc Maybe
The 'Maybe' type encapsulates an optional value. A value of type
@'Maybe' a@ either contains a value of type @a@ (represented as @'Just' a@),
or it is empty (represented as 'Nothing'). Using 'Maybe' is a good way to
deal with errors or exceptional cases without resorting to drastic
measures such as 'Prelude.error'.
The 'Maybe' type is also a monad. It is a simple kind of error
monad, where all errors are represented by 'Nothing'. A richer
error monad can be built using the 'Data.Either.Either' type.
Get suggestions for type holes
f :: String
f = _ "hello, world"
F.hs:2:5: error:
• Found hole: _ :: [Char] -> String
• In the expression: _
In the expression: _ "hello, world"
In an equation for ‘f’: f = _ "hello, world"
• Relevant bindings include f :: String (bound at F.hs:2:1)
Valid hole fits include
cycle :: forall a. [a] -> [a]
init :: forall a. [a] -> [a]
reverse :: forall a. [a] -> [a]
tail :: forall a. [a] -> [a]
id :: forall a. a -> a
(Some hole fits suppressed;
use -fmax-valid-hole-fits=N
or -fno-max-valid-hole-fits)
Plugin for Hole-driven development
Example: Type-Aware Code generation
Example: Case Splitting
Example: Tactics Metaprogramming
Introspect the Haskell heap
λ> value = "A Value"
λ> x :: (String, Bool, [Bool]) =
( value
, if head value == 'A' then value else ""
, cycle [True, False]
)
λ> :printHeap x
let x1 = _bco
x21 = []
in (x1,_bco,_bco)
-- evaluate everything
λ> length (take 100 (show x)) `seq` return ()
λ> :printHeap x
let x1 = "A Value"
x16 = True : False : x16
in (x1,x1,x16)
ghc-heap-view enables tools like ghc-vis for visualizing the heap
λ> :{
ones = [1,1..]
at 0 (x:xs) = x
at n (x:xs) = at (n-1) xs
:}
λ>
ghc-heap-view enables tools like ghc-vis for visualizing the heap
λ> :{
ones = [1,1..]
at 0 (x:xs) = x
at n (x:xs) = at (n-1) xs
:}
λ>
λ> at 1
λ> at 2
λ> at 3
ghc-heap-view enables tools like ghc-vis for visualizing the heap
λ> :{
ones = [1,1..]
at 0 (x:xs) = x
at n (x:xs) = at (n-1) xs
:}
λ>
λ> at 1
λ> at 2
λ> at 3
ghc-heap-view enables tools like ghc-vis for visualizing the heap
λ> :{
ones = [1,1..]
at 0 (x:xs) = x
at n (x:xs) = at (n-1) xs
:}
λ>
λ> at 1
λ> at 2
λ> at 3
HIE Files - coming soon to a GHC near you!
Go-To-Definition on non-local libraries
λ> :set -XTypeApplications -XDataKinds
λ> import GHC.TypeLits
λ> :kind '[] @Nat
'[] @Int :: [Nat]
λ> :kind 'Just @Nat
'Just @Nat :: Nat -> Maybe Nat
GHC 8.10.1 Release Announcement
Refinement types as a GHC plugin
someShortCircuitingFn :: Either SomeError SomeResult
someShortCircuitingFn = Either.do
result1 <- doThingOrFail
result2 <- doThingWithResult1OrFail result1
pure result2
someAsyncComputation :: Async ()
someAsyncComputation = Async.do
response1 <- makeAsyncRequest1
response2 <- makeAsyncRequest2
doSomething response1
doSomething response2
printThings :: IO ()
printThings = IO.do
input <- getLine
print input
Explicitly qualify the monad for do-notation
data Multiplicity
= One
| Many
newtype ReleaseMap = ReleaseMap (IntMap (Linear.IO ()))
-- | The resource-aware I/O monad. This monad guarantees that
-- acquired resources are always released.
newtype RIO a = RIO (IORef ReleaseMap -> Linear.IO a)
deriving (Data.Functor, Data.Applicative) via (Control.Data RIO)
unRIO :: RIO a %1-> IORef ReleaseMap -> Linear.IO a
unRIO (RIO action) = action
GHC 9.2.1-alpha2 Release Notes
and Beyond
Informal, opinionated, and conjectural considerations on scaling internal codebase and the ecosystem health
Some things to keep an eye on
Focus on internal maintenance of upstream Haskell packages
Focus on how it affects us
Some companies (I know of) making making major contributions to Haskell ecosystem:
More notable users of Haskell:
Alive and Well!
Focus on internal Haskell usage + libraries
Can we improve?
Definitely!
Should we improve?
🤷♂️
TVision Backend
Kinetiq
VidVita
ACRCloud
Tivo
Custom
TVision Backend
Kinetiq
VidVita
ACRCloud
Tivo
Custom
❓
❓
❓
Cons
We should definitely keep the monorepo, but there are tradeoffs:
Pros
Monorepo offers advantages to consider leaning into:
Other things to look into
Focus on how it affects us