All Projects → iokasimov → monopati

iokasimov / monopati

Licence: BSD-3-Clause license
Well-typed paths

Programming Languages

haskell
3896 projects

Projects that are alternatives of or similar to monopati

Userpath
Cross-platform tool for adding locations to the user PATH, no elevated privileges required!
Stars: ✭ 103 (+415%)
Mutual labels:  system, path
L2-Emulator
Implementing a Layer-2 Emulator in C using Graphs and LinkedList
Stars: ✭ 17 (-15%)
Mutual labels:  stack
file-icon-cli
Get the icon of a file or app as a PNG image (macOS)
Stars: ✭ 73 (+265%)
Mutual labels:  path
osutil
Go library to easily detect current operating system, current Linux distribution, macOS version and more...
Stars: ✭ 22 (+10%)
Mutual labels:  system
InterviewBit
Collection of solution for problems on InterviewBit
Stars: ✭ 77 (+285%)
Mutual labels:  stack
Pathslider
Numerical slider that follows a Bezier path
Stars: ✭ 15 (-25%)
Mutual labels:  path
shd
Show pretty HDD/SSD list
Stars: ✭ 37 (+85%)
Mutual labels:  system
is-valid-glob
Return true if a value is a valid glob pattern string, or array of glob patterns.
Stars: ✭ 21 (+5%)
Mutual labels:  path
URSA
[DEPRECATED] integrated ECS framework for Unity
Stars: ✭ 30 (+50%)
Mutual labels:  system
ngx-env
Easily inject environment variables into your Angular applications
Stars: ✭ 73 (+265%)
Mutual labels:  system
Stack-Lifecycle-Deployment
OpenSource self-service infrastructure solution that defines and manages the complete lifecycle of resources used and provisioned into a cloud! It is a terraform UI with rest api for terraform automation
Stars: ✭ 88 (+340%)
Mutual labels:  stack
svg-path-bbox
SVG paths bounding box calculator
Stars: ✭ 17 (-15%)
Mutual labels:  path
plugins
Elder.js plugins and community plugins.
Stars: ✭ 80 (+300%)
Mutual labels:  path
uC-TCP-IP
A compact, reliable, high-performance TCP/IP protocol stack. Features dual IPv4 and IPv6 support, an SSL/TLS socket option, and support for Ethernet, Wi-Fi, and PHY controllers.
Stars: ✭ 66 (+230%)
Mutual labels:  stack
AlgoDaily
just for fun
Stars: ✭ 118 (+490%)
Mutual labels:  stack
relative-path
Portable relative UTF-8 paths for Rust.
Stars: ✭ 54 (+170%)
Mutual labels:  path
ElevatorSystem
multi-elevator System
Stars: ✭ 13 (-35%)
Mutual labels:  system
myleetcode
♨️ Detailed Java & Python solution of LeetCode.
Stars: ✭ 34 (+70%)
Mutual labels:  stack
geeks-for-geeks-solutions
✅ My own Amazon, Microsoft and Google SDE Coding challenge Solutions (offered by GeeksForGeeks).
Stars: ✭ 246 (+1130%)
Mutual labels:  stack
stack-guard
A toy implementation of 'Stack Guard' on top of the LLVM compiler toolchain
Stars: ✭ 21 (+5%)
Mutual labels:  stack

Well-typed paths: revisited

Despite the fact that there are several “path” libraries in Haskell, I decided to write a new one I would like to use.

Problem description

Often (when you write a useful program) you need to do something to the filesystem. Using temporary files, reading directory contents, writing logs - in all of these cases you need to clarify the path. But path can be specified either in absolute or relative form, or be relative to some absolute path called home. And it can point either to a directory or a file. Instead of encoding these cases directly as type sums, we will do some trick.

The absolute path is just a path that relative to the root, the same for the home and current working directory. Let's create a type that indicates subject of relativity:

data Origin = Root | Now | Home | Early | Vague

And a sum type shows which object we point to:

data Points = Directory | File

We use stack as a core data structure for path, so our type is:

{-# language DataKinds, KindSignatures #-}

newtype Outline (origin :: Origin) (points :: Points) =
	Outline { outline :: Cofree Maybe String }

We can split our paths on groups:

type Incompleted = Relative | Current | Homeward | Previous
type Certain = Absolute | Current | Homeward | Previous

Now we need some rules that can help us build valid paths depending on theirs types. So, we can do this:

Incompleted Path To Directory + Relative Path To Directory = Relative Path To Directory
"usr/local/" + "etc/" = "usr/local/etc/"
Incompleted Path To Directory + Relative Path To File = Relative Path To File
"bin/" + "git" = "bin/git"
Absolute Path To Directory + Incompleted Path To Directory = Absolute Path To Directory
"/usr/local/" + "etc/" = "/usr/local/etc/" =
Absolute Path To Directory + Incompleted Path To File = Absolute Path To File
"/usr/bin/" + "git" = "/usr/bin/git"

But we can't do this:

_ Path To File + _ Path To File = ???
_ Path To File + _ Path To Directory = ???
Absolute Path To _ + Absolute Path To _ = ???
Incompleted Path To _ + Absolute Path To _ = ???

Based on these rules we can define two generalized combinators. Current, Homeward, Previous and Relative paths are the same internally, they are different only for type system.

(<^>) :: Incompleted Path To Directory -> Relative Path To points -> Relative Path To points
(</>) :: Absolute Path To Directory -> Incompleted Path To points -> Absolute Path To points

And, if you want improve your code readability, you can use specialized combinators:

-- Add relative path to incompleted path:
(<.^>) :: Current Path To Directory -> Relative Path To points -> Currently Path To points
(<~^>) :: Homeward Path To Directory -> Relative Path To points -> Homeward Path To points
(<-^>) :: Previous Path To Directory -> Relative Path To points -> Previous Path To points
(<^^>) :: Relative Path To Directory -> Relative Path To points -> Relative Path To points
-- Absolutize incompleted path:
(</.>) :: Absolute Path To Directory -> Current Path To points -> Absolute Path To points
(</~>) :: Absolute Path To Directory -> Homeward Path To points -> Absolute Path To points
(</->) :: Absolute Path To Directory -> Previous Path To points -> Absolute Path To points
(</^>) :: Absolute Path To Directory -> Relative Path To points -> Absolute Path To points

Get our hands dirty

There are some functions in System.Monopati.Posix.Calls that work with our Path definition:

  • As you may remember, we use Stack - it's an not empty inductive data structure. When we are in a root, we have no directory or file to point in - we are in the starting point, so current returns Nothing. We return absolute path because we actually want to know where we are exactly in the filesystem:
current :: IO (Maybe (Absolute Path To Directory))
  • Sometimes we want to change our current working directory for some reason. As documentation of System.Directory says: it's highly recommended to use absolute rather than relative paths cause of current working directory is a global state shared among all threads:
change :: Absolute Path To Directory -> IO (Absolute Path To Directory)
  • Creating and removing directories with absolute paths only:
create :: Absolute Path To Directory -> IO ()
remove :: Absolute Path To Directory -> IO ()

Simple example of usage

Let's imagine that we need to save some content in temporary files grouped on folders based on some prefix.

mkdir :: String -> IO (Absolute Path To Directory)
mkdir prefix = create $ part "Temporary" <^> part prefix

filepath :: String -> String -> IO (Absolute Path To File)
filepath filename prefix = (\dir -> dir </> part filename) <$> mkdir prefix

In this example, part function is like a pure for Path but it takes strings only. We create a directory and then construct a full path to the file.

As you can understand, this library motivates you use only absolute paths, but not force it.

Motivation of using this library

  • Concatenating paths is more efficient, instead of linear time for lists, it's Rope-like structure.
  • It uses two generalized combinators and six specialized instead of one for code readability. When you see them in the code you already know which paths you concatenate.

Well, it's easier for me to define what exactly I don't like in another "path"-libraries:

  • filepath - The most popular, but using raw strings.
  • path - TemplateHaskell (I really hate it), using raw strings in internals.
  • posix-paths - Focusing on performance instead of usage simplicity.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].