:PROPERTIES:
:ID:       d7d1398b-5b09-4eef-a193-3b2a9ca0abf7
:END:
#+title: Haskell
#+filetags: programmation

* Démarrer un projet
** Nix
[[file:Environnement%20Haskell%20avec%20Nix.md][Environnement Haskell avec Nix]]
** Script en un seul fichier (cabal)

=cabal run= sur le script suivant (ou chmod +x)

#+begin_src haskell
#!/usr/bin/env cabal
{- cabal:
build-depends: base
            , turtle
-}
{-# LANGUAGE OverloadedStrings #-}
import Turtle
main = echo "Hello World!"
#+end_src

** Script en un seul fichier (stack)
`stack run` sur le script suivant (ou chmod +x)
#+begin_src haskell
-- stack --resolver lts-6.25 script --package turtle
{-# LANGUAGE OverloadedStrings #-}
import Turtle
main = echo "Hello World!"
#+end_src
** Avec stack
#+begin_src sh
stack script simple.hs --resolver lts-14.18
#+end_src

* Librairies
** Data analysis : Frames
:PROPERTIES:
:CUSTOM_ID: data-analysis-frames
:END:
*** Minimal example
:PROPERTIES:
:CUSTOM_ID: minimal-example
:END:
#+begin_src haskell
{-# LANGUAGE DataKinds, FlexibleContexts, QuasiQuotes, TemplateHaskell, TypeApplications,
TypeApplications#-}
import Lens.Micro.Extras
import qualified Data.Foldable as F
import Frames

-- Data set from http://vincentarelbundock.github.io/Rdatasets/datasets.html
tableTypes "TLeft" "data/prestige.csv"
tableTypes "TRight" "data/test.csv"

loadLeft :: IO (Frame TLeft)
loadLeft = inCoreAoS (readTable "data/prestige.csv")

loadRight :: IO (Frame TRight)
loadRight = inCoreAoS (readTable "data/test.csv")


main = do
  l <- loadLeft
  r <- loadRight
  -- Native way of showing a row
  print $ frameRow l 1
  -- List-style way of showing a row
  print $ take 2 $ F.toList l

  let t = innerJoin @'[ColType] l r
  print $ frameRow t 1
#+end_src

*** Lire des fichiers tsv
:PROPERTIES:
:CUSTOM_ID: lire-des-fichiers-tsv
:END:
#+begin_example
{-# LANGUAGE DataKinds, FlexibleContexts, QuasiQuotes, TemplateHaskell, TypeApplications, OverloadedStrings #-}
module Main where

import qualified Control.Foldl                 as L
import qualified Data.Foldable as F
import           Frames
import Frames.TH (rowGen, RowGen(..))

tableTypes' (rowGen "ml-100k/u.user")
            { rowTypeName = "U2"
            , columnNames = ["index", "age", "sex", "job", "id"]
            , separator = "\t"}

loadRows :: IO (Frame U2)
loadRows = inCoreAoS (readTableOpt u2Parser "ml-100k/u.user")

main = do
  ms <- loadRows
  mapM_ print (F.toList  ms)
#+end_example

Il faut rajouter le parser "u2Parser" (cf
[[https://github.com/acowley/Frames/issues/178]])

** SQL: persistent
:PROPERTIES:
:CUSTOM_ID: sql-persistent
:END:
Pour se connecter à une base existante, il faut connaître le type de
colonnes (avec `.schema`). Puis définir un type.

*Important* on n'a pas besoin de remplir les types de toutes les
colonnes. Seulement celles qui nous intéressent !! Exemple: pour
org-roam, on regarde la table files

#+begin_src sql
sqlite> .schema files
CREATE TABLE files (file UNIQUE PRIMARY KEY, title , hash NOT NULL, atime NOT NULL, mtime NOT NULL);
#+end_src

#+begin_src haskell
{-# LANGUAGE TemplateHaskell            #-}
{-# LANGUAGE QuasiQuotes                #-}
{-# LANGUAGE TypeFamilies               #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE GADTs                      #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RecordWildCards            #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE DerivingStrategies         #-}
{-# LANGUAGE StandaloneDeriving         #-}
{-# LANGUAGE UndecidableInstances       #-}
{-# LANGUAGE DataKinds #-}

import qualified Database.Persist.TH as PTH
import Database.Persist (Entity(..))
import Database.Persist.Sql (toSqlKey)
import Data.Text
import Database.Persist.Sqlite
import Control.Monad.IO.Class
import Control.Monad.Logger

PTH.share [PTH.mkPersist PTH.sqlSettings, PTH.mkMigrate "migrateAll"] [PTH.persistLowerCase|
  File sql=files
    file Text
    Primary file
    title Text
    hask Text
    atime Text
    mtime Text
    deriving Show
|]

path =  "/home/alex/.emacs.d/.local/cache/org-roam.db"

main :: IO ()
main = runSqlite path $ do
    test <- selectList [] [LimitTo 1]
    liftIO $ print (test :: [Entity File])
#+end_src

Note: il faut définir une autre clé primaire, voir
[[https://hackage.haskell.org/package/persistent-2.14.5.0/docs/Database-Persist-Quasi.html]]

#+begin_src haskell
file Text
Primary file
#+end_src

Si la clé primaire est une chaîne de caractères

#+begin_src haskell
Id Text sql=id
#+end_src

Pour chercher par clé directement (toujours org-roam avec une clé en
chaine de caractère)

#+begin_src haskell
test <- get (NodeKey "1")
return $ (test :: Maybe Node)
#+end_src

** Pandoc
:PROPERTIES:
:CUSTOM_ID: pandoc
:END:
Voir [[file:Pandoc.md][Pandoc]] pour un exemple de customisation #
Éditeur ## Emacs

Utiliser haskell-compile Si on utilise haskell-proces-cabal-build
(default =C-c c-c=), il ne trouve pas le fichier .cabal associé quand on
éditer le code source

* Éditeur
** Emacs
:PROPERTIES:
:ID:       2e84138f-7559-4933-8e8b-345c5a03fe8b
:END:
Mode mal documenté (avec lsp)
C-c C-l pour charger code dans ghci
C-c C-z si on perd le popup