From afe595f4032ef40fec6b32e84e2874ca8b72033f Mon Sep 17 00:00:00 2001 From: Nick Leeman Date: Mon, 3 Apr 2023 13:57:42 +0200 Subject: [PATCH] initial push --- .gitignore | 1 + excersise.ts | 381 ++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 11 ++ tsconfig.json | 103 ++++++++++++++ 4 files changed, 496 insertions(+) create mode 100644 .gitignore create mode 100644 excersise.ts create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c43fe6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.js \ No newline at end of file diff --git a/excersise.ts b/excersise.ts new file mode 100644 index 0000000..b3bd7f0 --- /dev/null +++ b/excersise.ts @@ -0,0 +1,381 @@ +type FunType = { + (inputValue: input): output; + next: (nextFunction: FunType) => FunType; + repeat: (this: FunType, count: number) => FunType; + repeatUntil: (this: FunType, condition: FunType) => FunType; +} + +const Fun = (inputLamba: (_: input) => output): FunType => { + const func = inputLamba as FunType; + + func.next = function(nextFunction: FunType): FunType { + return Fun(inputValue => nextFunction(inputLamba(inputValue))); + } + + func.repeat = function(this: FunType, count: number): FunType { + if (count == 0) { + return Fun((x: input) => x); + } + + return this.next(this.repeat(count - 1)); + } + + func.repeatUntil = function(this: FunType, condition: FunType): FunType { + return Fun((x: input) => { + if (condition(x)) { + return x; + } + + console.log(x); + + return this.next(this.repeatUntil(condition))(x); + }) + + // Shorthand + // return Fun((x: input) => condition(x) ? x : this.next(this.repeatUntil(condition))(x)); + } + + return func; +} + + +const Conditional = function(_if: FunType, _then: FunType, _else: FunType): FunType { + return Fun((x) => { + if (_if(x)) { + return _then(x); + } else { + return _else(x); + } + }) +} + +// +// Some FunType lamdas +// +const isPositive: FunType = Fun((x: number) => x > 0); +const isEven: FunType = Fun((x: number) => x % 2 == 0); +const incr: FunType = Fun((x: number) => x + 1); +const double: FunType = Fun((x: number) => x * 2); +const decr: FunType = Fun((x: number) => x - 1); +const convert: FunType = Fun((x: number) => String(x)); +const exclaim: FunType = Fun((x: string) => x + "!"); +const saveSqrt = Fun((a: number) => a > 0 ? Math.sqrt(a) : Math.abs(Math.sqrt(a))); + +// +// Generic id functions +// + +// Id functions do basically nothing but initialize a base where we can work from (initializing a pipeline for example) +// Eg. Id functions are the base of all other functions +// const id: () => FunType = () => Fun(x => x); +function id(): FunType { + return Fun(_ => _); +} + +// Another way of writing this functions +const genericId = (): FunType => Fun(_ => _); + +// Even one more way of writing this function +const genericId2 = function(): FunType { + return Fun(_ => _); +} + +// Example usage 1 +let exampleId = id().next(incr).next(incr)(0) // -> result would be 2 +// | +// Define type here + +// Id functions but for a specific type (Non generic) +type Unit = {}; +const idNum: FunType = Fun(_ => 0); +const idString: FunType = Fun(_ => ""); +// | +// Define type here + +// Example usage 2 +let exampleId2 = idNum.next(incr).next(incr)(0) + +// +// Countainers / Functors +// + +// A functor is a container that is mappable +// Aka: We can run map functions on the containers itself to perform some kind of data update. + +type Countainer = { + content: content; // Content + counter: number; // For Example: amount of times object has been changed or mapped. +} + +// Increments the value of counter with 1 +function incrCountainer(countainer: Countainer): Countainer { + return { + content: countainer.content, + counter: countainer.counter + 1 + }; +} + +// Applies transformer lamda to content of countainer to potentionally map value to other type: content -> newContent +function mapCountainerNoFun(countainer: Countainer, transformer: FunType): Countainer { + return { + counter: countainer.counter + 1, + content: transformer(countainer.content) + } +} + + +// Applies transformer lamda to each element content of array to potentionally map value to other type: content -> newContent +function mapArray(transformer: FunType): FunType, Array> { + return Fun((array: Array) => array.map(transformer)); +} + +// Applies transformer lamda to content of countainer to potentionally map value to other type: content -> newContent +// !! But is wrapped in the FunType so we can use this in a pipeline !! +function mapCountainer(transformer: FunType): FunType, Countainer> { + return Fun(c => { + return { + counter: c.counter + 1, + content: transformer(c.content) + } + }) +} + +type Message = { + username: string; + likes: number; + message: string; +} + +// Some pipeline functions for Message type +const setUsername = (newUsername: string) => Fun(c => ({ username: newUsername, likes: c.likes, message: c.message })); +const incrLikes = () => Fun(c => ({ username: c.username, likes: c.likes + 1, message: c.message })); + +// We could also write these functions like this +function setMessage (msg: string): FunType { + return Fun(c => { + return { + username: c.username, + likes: c.likes, + message: msg + } + }) +} + +// Countainer where content is of Message Type +let messageCountainer: Countainer = { + counter: 0, + content: { + username: "", + likes: 0, + message: "" + } +} + +// Pipeline usage example with countainer and message +// Using mapCountainer +const messagePipeline1 = mapCountainer( + setUsername("Test") + .next(setMessage("This is a message")) + .next(incrLikes()) + .next(incrLikes()) +) + +const messagePipeline2 = mapCountainer( + setUsername("Test2") + .next(setMessage("This is a new and more improved message message")) + .next(incrLikes()) + .next(incrLikes()) + .next(incrLikes()) +) + +const message1 = messagePipeline1(messageCountainer); +const message2 = messagePipeline2(messageCountainer); + +//console.log(message1); +// Result: { +// counter: 1, +// content: { username: 'Test', likes: 2, message: 'This is a message' } +// } + +// This function will increase all the likes by 1 in a array of Countainers where its type is Message +// We use the MapArray function for this operation +const increaseAllLikes: FunType>, Array>> = mapArray(mapCountainer(incrLikes())); + +// Create array +const messages: Array> = [message1, message2]; + +// console.log(increaseAllLikes(messages)); +// Result: [ +// { +// counter: 2, +// content: { username: 'Test', likes: 3, message: 'This is a message' } +// }, +// { +// counter: 2, +// content: { +// username: 'Test2', +// likes: 4, +// message: 'This is a new and more improved message message' +// } +// } +// ] + +// Some more examples of array mapping + +// Basic increment +// console.log( +// mapArray(incr)([1, 2, 3, 4]) +//) +// Result: [ 2, 3, 4, 5 ] + +// Increment, double and decrement in pipeline +// console.log( +// mapArray(incr.next(double).next(decr))([1, 2, 3, 4]) +// ) +// Result: [ 3, 5, 7, 9 ] + +// Increment, check if even. +// Here we also perform a mapping function that will change the type of content +// console.log( +// mapArray(incr.next(isEven))([1, 2, 3, 4]) +// ) +// Result: [ true, false, true, false ] + +// +// Monoid / Monoidal structure / Monoids +// + +// Monoids are datatypes (structures) that supports a given mathematical operation plus some given values +// Examples: + +// + (Operator in numerical context) +// 1 + 2 == 2 + 1 +// (a + b) * c == c * (b + a) +// a + 0 == 0 + a == a + +// * (Operator in numerical context) +// (a * b) * c == a * (b * c) +// a * 1 == 1 * a == a + +// + (Operator in string context) +// (a + b) + c == a + (b + c) +// a + "" == "" + a == a + +// Practical examples with arrays +const numArray1: Array = [1, 2, 3]; +const numArray2: Array = [2, 3, 4]; +const numArray3: Array = [5, 6, 7]; +const numArray4: Array = []; + +const resultNumArr1 = numArray1.concat(numArray2.concat(numArray3)); // -> [1, 2, 3, 2, 3, 4, 5, 6, 7] +const resultNumArr2 = numArray1.concat(numArray2).concat(numArray3); // -> [1, 2, 3, 2, 3, 4, 5, 6, 7] +const resultNumArr3 = (numArray1.concat(numArray2)).concat(numArray3); // -> [1, 2, 3, 2, 3, 4, 5, 6, 7] +// Therefore: resultNumArr1 == resultNumArr2 == resultNumArr31 + +const resultNumArr4 = numArray4.concat(numArray1); // -> [1, 2, 3] +const resultNumArr5 = numArray1.concat(numArray4); // -> [1, 2, 3] +// Therefore: resultNumArr4 == resultNumArr5 + +// This type of reasoning also applies to (monoidal) functions!! +// Here monoids come into play + +let monoidTest1 = id().next(incr).next(incr).next(incr) (0); +let monoidTest2 = id().next(incr.next(incr).next(incr)) (0); +let monoidTest3 = id().next(incr.next(incr)).next(incr) (0); +// Therefore: monoidTest1 == monoidTest2 == monoidTest3 + +let monoidTest4 = incr.next(id()) (0); +let monoidTest5 = id().next(incr) (0); + +// Therefore: monoidTest4 == monoidTest5 + +// A monoido or triple is +// A type T +// An operator + : [T, T] => T +// An element z : T +// +// Such that: +// (a + b) + c == a + (b + c) <--- associative law +// a + z == z + a == a <--- identity law + +// +// Monads are different than monoids! +// Monads (NOT Monoid / Monodial, etc) are Functors and Monoids combined! +// + + +type Pair = { + fst: a, + snd: b +} + +const myPair: Pair = { + fst: 1, + snd: "Hello" +} + +function pairFst(): FunType, a> { + return Fun((pair: Pair) => { + return pair.fst; + }) +} + +const pairSnd = function(): FunType, b> { + return Fun((pair: Pair) => { + return pair.snd; + }) +} + +const pairMap = function(f1: FunType, f2: FunType): FunType, Pair> { + return Fun((pair: Pair) => { + return { + fst: f1(pair.fst), + snd: f2(pair.snd) + } + }) +} + + +type WithNum = Pair; + +const withNumSingle: WithNum = { + fst: "yeey", + snd: 3 +} + +const withNumDouble: WithNum> = { + fst: { + fst: "Hoi!", + snd: 3, + }, + snd: 1, +} + +const mapWithNum = function(f: FunType): FunType, WithNum> { + return pairMap(f, id()); +} + +const joinWithNum = function(): FunType>, WithNum> { + return Fun(num => { + return { + fst: num.fst.fst, + snd: num.snd + num.fst.snd + } + }) +} + +const incrementWithNum = function(): FunType, WithNum> { + return Fun((num: WithNum) => { + return { + fst: num.fst, + snd: num.snd + 1 + } + }) +} + + +console.log( + joinWithNum().next(incrementWithNum()).next(incrementWithNum()) (withNumDouble) +); + +const test123 = "sdsdds"; \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..c324a52 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "school-zooi", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..75dcaea --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,103 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +}