The bem
library is to generate classes in accordance with a scheme.
- to eliminate chances to misspell an entity because it is a type, not a string
- to make a combination of entities impossible unless it is defined by a scheme
- A modifier is a data constructor of a nullary sum type.
- A boolean modifier is a nullary modifier.
- A non-boolean modifier is a unary modifier whose only field is of a nullary sum type of nullary data constructors that are its values.
- An element is a data constructor of a
Type -> Type
GADT whose only type variable is a type of its optional modifiers. - A block is a data constructor
of the only
(Type -> Type) -> Type -> Type
GADT whose two type variables are types of its optional elements and its optional modifiers, respectively. - An entity is either a block, an element, or a modifier.
- A class is a generated string of a combination of some decorated entities.
- A generator is a field-function, using custom decorations, or a function, using the default decorations, from some entities combination defined by a scheme into something being or having a class.
- A class generator is a generator into a class.
- A scheme is all types of entities together.
- An entity part is a capitalised substring of a shown entity.
- A generated class is decorated according to a configuration.
- A scheme defines how and which entities can be combined.
- Absence of any elements or modifiers can be represented by any other appropriate type that is empty or its values are shown as the empty string in place of a GADT or a nullary sum type of them, respectively.
- Elements
that are alike or block-alike and modifiers
that are alike
must be prefixed with the prefix distinguishing them and
ending in the
"_"
suffix to avoid definition conflicts. (The prefix is trimmed from a generated class.) - The
bem-example
repository is the working source code of all the examples presented in this document. - If a modifier is neither boolean nor non-boolean, then it is interpreted as boolean, trimming its fields.
- If a value of a non-boolean modifier is not nullary, then all its fields are trimmed.
- In order that the regular
Gens
field-functions or the utilityGens
field-functions, using custom decorations can be used, they must be configured. (?)
- The distinguishing prefix of an entity should be the entity that it belongs to.
- 'Intr' stands for internal/intricate.
- All the examples are decorated according to the default settings.
- The default settings are described in Table 1.
- The settings in Table 1 are described regarding an entity as shown, that is, as a string.
Include this library into
- the .cabal file (?),
- the
default.nix
, - and the
shell.nix
of the dependee
Fetching this library at an appropriate commit-ish from a repository, include its derivation into a nix expression of the dependee.
- Include the
bem
package into thebuild-depends
field of the .cabal file of the dependee. - Pass the derivation of this library
as a value of the
bem
key into the third argument passed into acallCabal2nix
function.
Include the derivation of this library into the buildInputs
list.
Include the env
attribute
of the derivation
of this library
into the inputsFrom
attribute
of the attribute set
passed into the mkShell
function.
shell.nix
let
dvg-git = pin.dvg-git {};
fetch-hash = pin.fetch-hash {};
pin = import ./chr/pin.nix;
traverse = pin.traverse {};
nixpkgs = pin.nixpkgs {};
bem = pin.bem {};
ghcNixpkgs = pin.ghcNixpkgs {};
miso = pin.miso {};
in
{
pkgs ? nixpkgs,
ghcPkgs ? ghcNixpkgs,
}
:
pkgs.mkShell
{
buildInputs
=
[
dvg-git
fetch-hash
traverse
(pkgs.writeShellScriptBin
"watch"
(pkgs.lib.readFile ./scr/watch.sh)
)
pkgs.ghcid
];
inputsFrom
=
[
(miso.pkgs.haskell.packages.ghc865.callCabal2nix
"bem-example"
./.
{
inherit bem;
}
).env
];
}
Pass a lambda
returning a list
with the derivation
of this library
into a ghcWithPackages
function.
shell.nix
let
dvg-git = pin.dvg-git {};
fetch-hash = pin.fetch-hash {};
pin = import ./chr/pin.nix;
traverse = pin.traverse {};
nixpkgs = pin.nixpkgs {};
bem = pin.bem {};
ghcNixpkgs = pin.ghcNixpkgs {};
in
{
pkgs ? nixpkgs,
ghcPkgs ? ghcNixpkgs,
}
:
pkgs.mkShell
{
buildInputs
=
[
dvg-git
fetch-hash
traverse
(pkgs.writeShellScriptBin
"watch"
(pkgs.lib.readFile ./scr/watch.sh)
)
pkgs.ghcid
(ghcPkgs.haskell.packages.ghc865.ghcWithPackages (_: [bem]))
];
}
- If the dependee is compiled
with the
GHCJS
compiler, then apply the function from thedefault.nix
file of this library to an attribute set with theuseGhcjs
key set to thetrue
value.
- Define a scheme. (?)
- Generate classes
with the default decorations (?)
- or configure (?)
- either the regular class generators
- or the utility
Gens
class generators
- and generate classes with custom decorations. (?)
- or configure (?)
- Define one type of modifiers for each block or element that has ones.
- Derive a
Show
instance for each type of modifiers. - Define one
Type -> Type
GADT of elements for each block. - Derive a
Show
instance for each GADT of elements. - Define an only
(Type -> Type) -> Type -> Type
GADT of blocks. - Derive a
Show
instance for the GADT of blocks.
src/Bem/Scheme.hs
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE StandaloneDeriving #-}
module Bem.Scheme where
import Bem.Utl.Utl
import Data.Kind
data Blk (e :: Type -> Type) b where
Btn :: Blk NoElem BtnMod
Header :: Blk HeaderElem HeaderMod
Logo :: Blk NoElem NoMod
Root :: Blk RootElem NoMod
Search :: Blk SearchElem SearchMod
TextInput :: Blk NoElem TextInputMod
deriving instance Show (Blk e b)
data BtnMod = Btn_Dark deriving Show
data HeaderMod = Header_Dark deriving Show
data HeaderElem m where
Header_Logo :: HeaderElem NoMod
Header_Search :: HeaderElem NoMod
deriving instance Show (HeaderElem m)
data RootElem m where
Root_Header :: RootElem NoMod
Root_Logo :: RootElem NoMod
deriving instance Show (RootElem m)
data SearchBtnMod = SearchBtn_Size Size deriving Show
data SearchTextInputMod = SearchTextInput_Size Size deriving Show
data SearchElem m where
Search_Btn :: SearchElem SearchBtnMod
Search_TextInput :: SearchElem SearchTextInputMod
deriving instance Show (SearchElem m)
data SearchMod = Search_Dark deriving Show
data Size = Big | Small deriving Show
data TextInputMod = TextInput_Dark deriving Show
src/Bem/style.sass
.btn
.btn_dark
.header
.header_dark
.header__search
.logo
.logo_dark
.search
.search_dark
.search__btn
.search__btn_size_big
.search__btn_size_small
.text-input
.text-input_dark
- Absence of the dark modifier on a block
search__btn
element would mean that it is light, which is set directly on it. - Absence of the size modifier on a
search__btn
element would mean that it is of the medium size, which is set directly on it. - Derive a
Show
instance for a GADT using theStandaloneDeriving
extension.
- The
NoElem
empty type constructor can be used to denote absent elements. - The
NoMod
empty type can be used to denote absent elements/modifiers.
Apply the init
function
to an appropriate Cfg
record
assignining the result a name
in order that
the resulting configured class generators can be referenced
with it.
src/Bem/Init.hs
module Bem.Init where
import Bem.Cfg.Cfg
import qualified Bem.Cls.Gen.Cfg as Bem
gens :: Bem.Gens
gens = Bem.init defCfg
- If a particular combination of setting values is occuried, then some settings are defaulted back to avoid malformed custom configurations.
- Apply the
init
function to thedefCfg
value to avoid setting up an entireCfg
record using some defaults. - In order that the resulting
Gens
record contains the utility class generators, make it using the utilityinit
function in place of the regular one.
Apply a corresponding class generator to an entities combination defined by a scheme.
src/Main.hs
import Bem.Scheme
import Bem.Bem
import Bem.Utl.Utl
main :: IO ()
main = do
print $ genBlk Btn [Btn_Dark] Search Search_Btn [SearchBtn_Size Big]
print $ decorSingleton Root
Apply a corresponding configured class generator,
referencing its Gens
record
with the name
assigned during configuring,
to a combination
of some entities
from a scheme.
src/Main.hs
import Bem.Scheme
import qualified Bem.Bem as Bem
import Bem.Utl.Utl
import Bem.Init
main :: IO ()
main = do
print $ Bem._genBlk gens
Btn [Btn_Dark] Search Search_Btn [SearchBtn_Size Big]
print $ decorSingleton Root
- The example prints the
"btn btn_dark search__btn search__btn_size_big"
string. - The utility class generators can be used instead of the regular class generators to avoid redundant modifier passing.
- The
decorSingleton
function can be used to generate a class for the topmost BEM block to avoid passing it as a string.
Run the nix-build
command.
Run the nix-shell --pure --run watch
command.
- All the commands must be executed from the project root directory
or supplied with a path to it
or either the
default.nix
or theshell.nix
script.
- New source code groups introduced by a new commit are placed after old ones.
- Description
- Installation flow
- Installation
- Usage flow
- Usage
- Contributing (optional)
- Convention (optional)
- Defined scopes (optional)
- Appendix (optional)
- Body
- Features
- Terms (optional)
- Hints (optional)
- Stipulations (optional)
- Body
- Notes (optional)
- Hints (optional)
- A too long part of speech is split moving each occurrence of its inner part of speech onto the next line along with the ending comma and the rest of it onto the line after the next line till it fits 80 characters or no inner part of speech is left to split on.
If the order of some parts of speech does not have to be specific, then they are ordered in the ASCII order but ignoring special characters.
- sentence
- interjection
- adverbial
- clause
- participle phrase
- coordinating conjunction
- preposition
- coordinate adjective
- Local
import
s are separated from external ones with one line. - Source code groups are separated with two lines.
type
s,newtype
s,data
s, top-level-bindings are separated with one line.
- The module
ending in the
<A>.<A>
suffix is a high-level module either exporting or reexporting high-level API beneath itself except for the modules ending in theUtl.Intr
orUtl.Utl
suffix. - The module
ending in the
<A>.<B>
suffix is the module ending in the<A>.<A>
suffix extended with the capabilities of the module ending in the<B>.<B>
suffix. - The module
ending in the
<A>.Intr
suffix is a low-level module exporting low-level API for the module - The module
ending in the
Utl.Intr
orUtl.Utl
suffix is a utility module exporting extra utilities. - If an
import
of the local low-level module ending in the<A>.Intr
suffix introduces definition conflicts, then it isqualified as Intr<A>
. - If an
import
of the local high-level module ending in the<A>
suffix introduces definition conflicts, then it isqualified as <A>
.
- Items of a source code group are ordered in the ASCII order.
- Source code groups are ordered from the more abstract to the less abstract,
qualified
import
s are placed beneath non-qualified
ones.
- file-header pragmas
export
simport
stype
snewtype
sdata
s- top-level bindings
if changes are made
inside a src/Bem/<scope>/<scope>.hs
/src/Bem/<A>/<scope>.hs
path,
then they are made
within a <scope>.
- A part of speech is moved onto its next line along with the conjunction introducing it.
- All the high-level utilities and the low-level utilities are optional, that is, all the features work without them.
- An image or a table has its own two-level heading of its type and number.
- Appendix, denoted with a horizontal line, contains images and tables.
- The source code groups are listed from the more abstract to the less abstract.
instance
s of anewtype
or adata
are defined after it and before the next one and ordered in the ASCII order.
- cfg
- cls
- utl
the available settings
Setting | Description | Default value |
---|---|---|
_elemSep |
a separator between a block and its element | "__" |
_modSep |
a separator between a non-modififer entity, a modifier, and an optional value of the latter | "_" |
_partSep |
a separator between entity parts | "-" |
_partsAreCptled |
whether to capitalize entity parts | False |
the possible custom configuration malformations and corresponding defaulted settings
Custom configuration malformation | Defaulted settings |
---|---|
There are letters in the value of the _partSep field. |
_partSep |
There are the same separators. | the same separators |
There is no actual separation, that is, the value of the _partSep field is "" and the value of the _partsAreCptled field is False . |
_partSep |