--- title: Monad in C# - 淺談 # 標題 description: Monad in C# - 淺談# 描述 image: # 封面 tags: programming # 內部標籤 robots: index, nofollow # [no]index, [no]follow lang: zh # language dir: ltr # left to right or right to left breaks: # newline style GA: UA-168653916-1 # google analysis disqus: hackmd slideOptions: transition: fade # https://revealjs.com/transitions/ theme: back --- # Monad in C# - 淺談 ## 前言 在開始講```monad```之前 希望你能夠多少對於Lambda 有一定程度的了解 這對於理解```monad```會有所幫助 以及```monad```可能會對於我們在思考問題的方法上 有很大的衝擊性的改變 如果你有辦法建立自己對於```monad```的理解的話 ## 情境 - 計算機 想像一下一個簡單的計算機程式具有簡單的四則運算 總共有四個方法 add, subtract, multiply, divide 簡單實現如下 ```csharp public static class Calculation { public static int Add(int left, int right) { return left + right; } public static int Subtract(int left, int right) { return left - right; } public static int Multiply(int left, int right) { return left * right; } public static int Divide(int left, int right) { return left / right; } } ``` 當我們要使用這些方法時可能我們會這樣使用 假設我們要帶入一個參數為 x 並且運用剛剛寫好的方法如下 ```csharp public int Compute(int x) { int a = Add(x, 2); int b = Multiply(a, 3); int c = Subtract(b, 4); return c; } ``` 上述的方法如果寫成一般的數學運算式(不是lambda喔!) 則是 ```f(x) = (x + 2) * 3 - 4``` 而在需求總是在變化的情況下 上述的做法將受到非常嚴重的挑戰 例如下面幾種情境: - Multiply從3 變成 5 - Subtract改成Divide - Add與 Multiply中間必須加一個新的算法餘數(%) 及其參數值為外部帶入 也許可能你會覺得不過就只是變成下面這種情況 ```csharp public int Compute(int x, int m) { int a = Add(x, 2); int b = a % m int c = Multiply(b, 5); int d = Divide(c, 4); return d; } ``` 以上動作不知道已經違反多少個開發原則 已及惹怒了多少個引用此方法的人 這並不總是表示提出需求改變的那一方很機掰 我們在做計算機這個功能時也存在著不夠好的情境 而也就是我這次想要講的事情 ## 分析 關於```Calculation```中的方法該如何使用其實不是只有直接調用這一種方法 我們可以透過把方法簽章當成一種類型來檢視我們所定義的方法 方法簽章是指方法在調用時提供的資訊 例如 方法名稱 參數 回傳值 等等不涉及方法詳細運行過程的資訊 例如```Add```這個方法 就是 名稱為 Add, 參數為 整數 left, 整數 right, 以及 回傳值型別為整數 這就像是在C# 中的 ```Func<int, int, int> Add```; 而同樣我們也發現 Subtract, Multiply, Divide 的簽章也都剛好與Add相同 所以 這四個方法我們就都可以把它視為 ```Func<int, int, int>``` 此時或許我們可以建立一個新的類別如下 ```csharp public class Unit { public Unit(int unit) { Current = unit; } public int Current { get; } public Unit Append(Func<int, int, int> func, int right) { return new Unit(func(Current, right)); } } ``` 這樣我們就可以寫 ```csharp new Unit(x) .Append(Add, 2) .Append(Multiply, 3) .Append(Subtract, 4) ``` 這樣我們在需求改變時則會是 ```csharp new Unit(x) .Append(Add, 2) .Append(x => x % m) .Append(Multiply, 5) .Append(Divide, 4) ``` 但到目前為止仍然多少還是會受需求改變的影響 所以從嚴格的標準來看其實跟一開始的沒有多少差別 理想的情況應該把 Unit 在進一步的做調整如下 ```csharp public class Unit { public Unit(int unit) { Current = unit; } public int Current { get; } public Unit Append(Func<int, int, int> func) { return new Unit(func(Current, right)); } public Unit AppendMany(params Func<int, int>[] funcs) { return new Unit(funcs.Aggregate(Current, (current, func) => func(current))); } } ``` 在```AppendMany```中我們可以一次加入多個運算方法 但每一個運算方法可能都有其需要帶入的參數 此時就會運用到```Curry```這個技術 實現如下 ```csharp public static Func<int, int> Curry(Func<int, int, int> func, int right) { return x => func(x, right); } ``` 所以最後就可以寫成如下 ```csharp //需求修改前 new Unit(x).AppendMany( Curry(Add, 2), Curry(Multiply, 3), Curry(Subtract, 4) ) //需求修改後 new Unit(x).AppendMany( Curry(Add, 2), Curry(x => x % m), Curry(Multiply, 5), Curry(Divide, 4) ) ``` 最後 我們可以另外將這些運算從方法中移出 ```csharp Func<int, int>[] before = new Func<int, int>[] { Curry(Add, 2), Curry(Multiply, 3), Curry(Subtract, 4) } Func<int, int>[] after = new Func<int, int>[] { Curry(Add, 2), Curry(x => x % m), Curry(Multiply, 5), Curry(Divide, 4) } new Unit(x).AppendMany(before); new Unit(x).AppendMany(after); ``` 這樣我們就可以對於這個運算流程做像是序列化或是給予不同的定義 甚至我們可以創造新的流程定義來做為帶入的參數 並且我們可以對於每一個算法單獨進行測試 來提升其可行性 而這就是其中一種提升可擴展性 可維護性 可讀性的方法之一 ## 為何我們要做這種設計 想像一下近期在工作上最常講的幾個關鍵字 ex: 迭代, 排程, SOP 當我們把看似連續的過程轉變成為一個個具階段性的步驟時 他就會變得更容易地知道是哪裡有問題 更容易做改變 並且讓整個流程變得更立體 更符合人類腦袋中三次元的思維 在整體的維護和可讀性上就有辦法做到有效的提升 ## 該怎麼把這種範例套用到現實的工作上 已這次的案例```Unit```中的值為```Current``` 而```Current```的型別為整數 所以我們可以設計一個 ```Unit<T>```做法如下 ```csharp public class Unit<T> { public Unit(T unit) { Current = unit; } public T Current { get; } public Unit Next(params Func<T, T>[] funcs) { return new Unit(funcs.Aggregate(Current, (current, func) => func(current))); } } ``` 而當你宣告了不同的T 就可以依照T來產生特定的工作流程 ## 名稱定義 到目前為止其實已經幾乎講完Monad 最常見的幾種定義 functor: 就是```Next```中的參數 funcs 的element 也就是 ```Func<T, T>``` monoid: 就是```Unit<T>``` ## 現實中的案例 很多屬於```monad``` 這項技術的實現其實常常都是很好用的套件 例如: ```Linq```,```Rx```(```.net```, ```js```, ...etc) 但 ```monad``` 不僅止於此 我們可以將系統設計成具有這種特性的架構 那麼整個系統就會具有```monad```的優勢 而事實上這是來自於```Functional Programming``` 的技術 但即使我們使用的是OO 不代表就無法使用並獲得他的優勢 # 後記 此篇僅為淺談 透過一些簡單的實現 可能有的人已經在使用這一的模型 包括先前我在研究關於```Pipe``` 的實現 都是與此有關的事情 真的要完整介紹整個 ```monad``` 理論 與相關應用 像是 ```Context(monoid)``` 的概念, 如同 ```Linq```的 延遲執行, ```aspnetcore``` ```Startup```中的 ```Middleware pipeline``` 等等非常非常多 並且這些都是可以讓我們負責的邏輯 變得簡單(simple not easy) 更好理解的程式 # 備註 一個複雜的系統不一定會因為他用```easy```的寫法而變得 ```simple``` 因為一個系統之所以為simple是因為該系統於閱讀時所需要持續累積的內容很少 而為什麼會說長方法應該要將方法拆出 是因為我們每閱讀一行程式碼 就會在腦袋中去堆疊他 我們透過拆散成方法的作法 如同資料庫建立索引那樣 讓我們可以透過索引從更好的起點開始堆疊 所以方法名稱被用來提示方法內容 方法參數被用來提示已知內容 而這件事情也被衍生到類別 像是 ```SRP``` 也甚至衍生到```module``` 像是 ```one class per file``` 所以當所有的Category 都只做一件事情並持續整合時 就會相對容易找到有問題的內容 再加上log 輔助 維護 可讀性 就會提升 這樣子的架構設計並不是足夠easy的做法 但是有足夠明確原則之下 在複雜的東西 都可以實現出足夠simple的系統 而且simple的系統會伴隨著 - simple to read - simple to manage - simple to change 而easy往往可能會在後續變得 hard to manage and hard to change <style> /*--------------- view ---------------*/ body[style], body[style*="background-color: white;"] { background-color: #1e1e1e !important; } body { color: #abb2bf; } .ui-view-area, .markdown-body, .ui-content { background: #1e1e1e; color: #abb2bf; } h1, h2, h3, h4, h5, h6, p { color: #ddd; } hr { border-color: #6d6d6d; } /* form */ .form-control { background: #333; color: #fff; } .form-control::placeholder, .form-control::-webkit-input-placeholder, .form-control:-moz-placeholder, .form-control::-moz-placeholder, .form-control:-ms-input-placeholder { color: #eee; } /*--------------- navbar ---------------*/ .header { background-color: #0e0e0e; border-color: #0e0e0e; } .navbar { background-color: #0e0e0e; border-color: #0e0e0e; } .navbar a { color: #eee !important; } .navbar .btn-group label { background-color: #0e0e0e; color: #eee; border-color: #555; } .navbar .btn-group label.btn-default:focus, .navbar .btn-group label.btn-default:hover { background-color: #2a2a2a; color: #eee; border-color: #555; } .navbar .btn-group label.active { background-color: #555; color: #eee; border-color: #555; } .navbar .btn-group label.active:focus, .navbar .btn-group label.active:hover { background-color: #555; color: #eee; border-color: #555; } .navbar-default .btn-link:focus, .navbar-default .btn-link:hover { color: #eee; } .navbar-default .navbar-nav>.open>a, .navbar-default .navbar-nav>.open>a:focus, .navbar-default .navbar-nav>.open>a:hover { background-color: #555; } .dropdown-header { color: #aaa; } .dropdown-menu { background-color: #222; border: 1px solid #555; border-top: none; } .dropdown-menu>li>a { color: #eee; } .dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover { background-color: #555555; color: #eee; } .dropdown-menu .divider { background-color: #555; } .header .open .dropdown-menu { background-color: #202020; } .navbar .announcement-popover { background: #4F4F4F; } .navbar .announcement-popover .announcement-popover-header { background: #2e2e2e; border-bottom: 1px solid #2e2e2e; } .navbar .announcement-popover .announcement-popover-body { background: #4F4F4F; color: #eee; } .navbar .announcement-popover .announcement-popover-footer { background: #4F4F4F; } .navbar .announcement-area .caption.inverse { color: #eee; } .label-warning { background-color: #ffc107; color: #212529; } /*--------------- history / recent ---------------*/ .list.row-layout li .item { border-color: #696c7d; } .list.row-layout li:nth-last-of-type(1) .item { border-bottom: none; } .list li .item { background: #1c1c1c; } .list li:hover .item, .list li:focus .item { background: #404040; } .list li .item h4 { color: #fff; } .list li p { color: #ccc; } .list li p i { font-style: normal; } .list li .item .content .tags span { background: #555; } .list li .item.wide .content .title a, .list li .item.wide .content .title a:focus, .list li .item.wide .content .title a:hover { color: #ddd; } .ui-item { color: #fff; opacity: 0.7; } .ui-item:hover, .ui-item:focus { opacity: 1; color: #fff; } .list li .item.wide hr { border-color: #6d6d6d; } .overview-widget-group .btn, .multi-select-dropdown-menu .ui-dropdown-label, .multi-select-dropdown-menu .dropdown-options, .form-control { border-color: #6d6d6d; } .multi-select-dropdown-menu .dropdown-options .ui-option:hover { background-color: #4d4d4d; color: #eee; } #overview-control-form #overview-keyword-input-container .select2-container { background-color: #3e4045 !important; } #overview-control-form #overview-keyword-input-container .select2-container .select2-choices { background-color: #3e4045; } .search { background-color: #3e4045; color: #eee; } .btn.btn-gray { background: #1b1b1b; } .btn.btn-gray:hover { background: #4d4d4d; color: #eee; } .search::placeholder, .search::-webkit-input-placeholder, .search:-moz-placeholder, .search::-moz-placeholder, .search:-ms-input-placeholder { color: #eee; } .btn.btn-gray { border-color: #6d6d6d; background: #333; color: #eee; } .select2-default { color: #eee !important; } .select2-results .select2-highlighted { background: #4d4d4d; color: #eee; } .select2-container-multi .select2-choices { background: #3e4045; } .select2-container-multi .select2-choices .select2-search-choice { background: #131313; color: #eee; border-color: #555; box-shadow: none; } .btn-default, .btn-default:focus { color: #eee; background-color: #2e2e2e; border-color: #6a6a6a; } .btn-default.active.focus, .btn-default.active:focus, .btn-default.active:hover, .btn-default:active.focus, .btn-default:active:focus, .btn-default:active:hover, .open>.dropdown-toggle.btn-default.focus, .open>.dropdown-toggle.btn-default:focus, .open>.dropdown-toggle.btn-default:hover { background: #737373; } .btn-default:hover { color: #fff; background-color: #7d7d7d; border-color: #6a6a6a; } .overview-widget-group .btn.active { background-color: #6a6a6a; color: #eee; } .overview-widget-group .btn:hover { background-color: #7d7d7d; color: #eee; border-color: #636363; } .overview-widget-group .slider.round { border-color: #ccc; } .overview-widget-group .slider.round:before { border-color: #ccc; } .overview-widget-group input:checked+.slider { background-color: #ccc; } .ui-category-description-icon a { color: #eee; } .item .ui-history-pin.active { color: #f00; } .ui-history-close { color: #eee; opacity: 0.5; } .pagination>li>a, .pagination>li>span { color: #eee; background-color: #2e2e2e; border-color: #6a6a6a; } .pagination>li>a:hover { color: #fff; background-color: #7d7d7d; border-color: #6a6a6a; } .pagination>.disabled>a, .pagination>.disabled>a:focus, .pagination>.disabled>a:hover, .pagination>.disabled>span, .pagination>.disabled>span:focus, .pagination>.disabled>span:hover { color: #eee; background-color: #2e2e2e; border-color: #6a6a6a; } .pagination.dark>li>a, .pagination.dark>li>span { color: #aaa; } /*--------------- settings ---------------*/ .section .form-horizontal .form-group .btn-default { font-size: 16px; border-color: #6d6d6d; background-color: #333; color: #FFF; } .section .form-horizontal .form-group .btn-default:hover, .section .form-horizontal .form-group .btn-default:focus { background-color: #737373; color: #FFF; } .section .form-horizontal .form-control:focus { border-color: #bbb; } /*--------------- share view ---------------*/ #notificationLabel, .ui-infobar .btn.ui-edit { color: #eee; border-color: #6a6a6a; } .ui-infobar__user-info li { color: #bbb; } footer { background: #101010; color: #bbb; border-top: 1px solid #454545; } footer a { color: #bbb; } /*--------------- doc view ---------------*/ .markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6, .markdown-body hr, #doc>h1 { color: #ddd; border-color: #777 !important; } .h1 .small, .h1 small, .h2 .small, .h2 small, .h3 .small, .h3 small, .h4 .small, .h4 small, .h5 .small, .h5 small, .h6 .small, .h6 small, h1 .small, h1 small, h2 .small, h2 small, h3 .small, h3 small, h4 .small, h4 small, h5 .small, h5 small, h6 .small, h6 small { color: #ddd; } .markdown-body p { color: #ddd; } .markdown-body a { color: #7bf; } .markdown-body a { color: #7bf !important; } .markdown-body ul li, .markdown-body ol li { color: #ddd; } .markdown-body blockquote { color: #ddd; border-left-color: #777; font-size: 16px; } .markdown-body code, code { color: #dfdfdf !important; background-color: #424a55; } .markdown-body pre { background-color: #1e1e1e; border: 1px solid #555 !important; color: #dfdfdf; } blockquote .small, blockquote footer, blockquote small { color: #bbb; } .mark, mark { background-color: rgba(255, 255, 0, 0.32) !important; color: #ddd; margin: .1em; padding: .1em .2em; } /* Todo list */ .task-list-item-checkbox { margin: 0.18em 0 0.2em -1.3em !important; } .task-list-item input[type=checkbox] { -webkit-appearance: none; -moz-appearance: none; appearance: none; position: relative; top: -1px; margin: 0 1rem 0 0; cursor: pointer; } .task-list-item input[type=checkbox]::before { -webkit-transition: all 0.1s ease-in-out; -moz-transition: all 0.1s ease-in-out; transition: all 0.1s ease-in-out; content: ""; position: absolute; left: 0; z-index: 1; width: 16px; height: 16px; border: 2px solid #F44336; } .task-list-item input[type=checkbox]:checked::before { -webkit-transform: rotate(-48deg); -moz-transform: rotate(-48deg); -ms-transform: rotate(-48deg); -o-transform: rotate(-48deg); transform: rotate(-48deg); height: 9px; border-color: #00E676; border-top-style: none; border-right-style: none; } .task-list-item input[type=checkbox]::after { content: ""; position: absolute; top: -0.125rem; left: 0; width: 16px; height: 16px; background: #333; cursor: pointer; } /* table */ .markdown-body table tr { background-color: #1e1e1e; border-top: none; border-bottom: 1px solid rgba(255, 255, 255, 0.3); } .markdown-body table tr:first-child { border-top: 1px solid rgba(255, 255, 255, 0.2); } .markdown-body table tr:nth-child(2n) { background-color: #333; } .markdown-body table tr th { color: #64B5F6; } .markdown-body table th, .markdown-body table td { border: none; } .markdown-body table tr th:first-child, .markdown-body table tr td:first-child { border-left: 1px solid rgba(255, 255, 255, 0.1); } .markdown-body table tr th:last-child, .markdown-body table tr td:last-child { border-right: 1px solid rgba(255, 255, 255, 0.1); } .markdown-body table tr td { color: #ddd; } .markdown-body pre.flow-chart, .markdown-body pre.sequence-diagram, .markdown-body pre.graphviz, .markdown-body pre.mermaid, .markdown-body pre.abc { background-color: #fff !important; } /* alert */ .alert h1, .alert h2, .alert h3, .alert h4, .alert h5, .alert h6, .alert p, .alert ul li, .alert ol li { color: #31708f; } .alert a { color: #002752; font-weight: 700; } .alert h1:first-child, .alert h2:first-child, .alert h3:first-child, .alert h4:first-child, .alert h5:first-child, .alert h6:first-child { margin-top: 0; } .markdown-body .alert>p { margin-top: 0px; margin-bottom: 10px; } .markdown-body .alert>ul, .markdown-body .alert>ol { margin-bottom: 16px; } .markdown-body .alert>*:last-child { margin-bottom: 0; } .alert-warning { background-color: #fff3cd; border-color: #ffeeba; } /* scroll bar */ .ui-edit-area .ui-resizable-handle.ui-resizable-e { background-color: #303030; border: 1px solid #303030; box-shadow: none; } /* info bar */ .ui-infobar { color: #999; } /* permission */ .permission-popover-btn-group .btn.focus, .permission-popover-btn-group .btn:active, .permission-popover-btn-group .btn:focus, .permission-popover-btn-group .btn.active { background-color: #6a6a6a !important; color: #eee !important; border-color: #555 !important; } .permission-popover-btn-group .btn:hover, .permission-popover-btn-group .btn.active:hover { background-color: #7d7d7d !important; color: #eee !important; border-color: #636363 !important; } .ui-delete-note a:hover, .ui-delete-note a:focus, .ui-delete-note a:active { background-color: #dc3545 !important; } .ui-invitee-invite { border-color: #6a6a6a !important; } .ui-invitee-invite:hover, .ui-invitee-invite:focus { background-color: #737373; color: #eee !important; } .ui-invitee.ui-invitee-list .ui-invitee-remove, .ui-invitee.ui-invitee-list .ui-invitee-remove:hover, .ui-invitee.ui-invitee-list .ui-invitee-remove:focus, .ui-invitee.ui-invitee-list .ui-invitee-remove:active { background-color: #dc3545; border: 1px solid #dc3545; } .select2-container { background: #202020; } .select2-container-multi .select2-choices .select2-search-field input { color: #eee; } .select2-container-multi .select2-choices .select2-search-field input.select2-active { color: #000; } .select2-drop { background: #202020; color: #eee; } .select2-results .select2-no-results, .select2-results .select2-searching, .select2-results .select2-ajax-error, .select2-results .select2-selection-limit { background: #202020; } /* table of contents block*/ .ui-toc-dropdown { width: 42vw; max-height: 90vh; overflow: auto; text-align: inherit; } /* table of contents text*/ .ui-toc-dropdown .nav>li>a { font-size: 14px; font-weight: bold; color: #ddd; } /* table of contents text: active*/ .ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a { color: #7bf; border-left-color: #7bf; } /* table of contents text: focus, hover*/ .ui-toc-dropdown .nav>li>a:focus, .ui-toc-dropdown .nav>li>a:hover { color: #7bf; border-left-color: #7bf; } /* drop down floating table of contents */ .ui-toc-dropdown.dropdown-menu { background: #333; } .toc-menu a { color: #ddd; } .toc-menu a:focus, .toc-menu a:hover { color: #7bf; } /*--------------- editor ---------------*/ .cm-m-markdown { color: #ddd; } .cm-s-one-dark .cm-header, .cm-m-xml.cm-attribute { color: #ffa653; } .cm-s-one-dark .cm-string, .cm-s-one-dark .cm-variable-2 { color: #7bf; } .cm-m-markdown.cm-variable-3 { color: #ff7e7e; } .cm-s-one-dark .cm-link { color: #b0ee83; } .cm-s-one-dark .CodeMirror-linenumber { color: #666; } .cm-strong { color: #f4511e; } .cm-s-one-dark .cm-comment { color: #a9a9a9; } .cm-matchhighlight { color: #ffea00; } .cm-positive { color: #11bf64; } .cm-negative { color: #ff3e3e; } .dropdown-menu.CodeMirror-other-cursor { border: 2px solid #4d4d4d; background-color: #202020; } .dropdown-menu.CodeMirror-other-cursor li a { color: #ececec; } /*--------------- book mode ---------------*/ .topbar { background: #1e1e1e; } .btn.focus, .btn:focus, .btn:hover { color: #aaa; } .summary { background: #1e1e1e; } .summary, .toolbar { background: #1e1e1e !important; border-color: #4d4d4d !important; } .toolbar i { color: #fff; } .summary h1, .summary h2, .summary h3 .summary hr { color: #ddd; border-color: #777 !important; } .summary .nav>li>a { color: #7bf; } .summary .nav-pills>li.active>a, .summary .nav-pills>li.active>a:focus, .summary .nav-pills>li.active>a:hover { color: #ff9100; } .ui-summary-search { font-size: 16px; border: 1px solid #6D6D6D; background-color: #333; color: #FFF; } .summary h1, .summary h2, .summary h3, .summary h4, .summary h5, .summary h6 { border-color: #454545; } /* fix body background color to dark */ div[class$=container-mask] { background: #1e1e1e; z-index: 1; display: block; } /* notification */ .dropdown.ui-notification .ui-notification-label, .dropdown.ui-invitee .ui-invitee-label { color: #eee; border-color: #6a6a6a; } .ui-notification .dropdown-menu { border-top: 1px solid #555; } /*--------------- help ---------------*/ .modal-header { background-color: #2a2a2a; } .panel-default { border-color: #6d6d6d; } .panel-default>.panel-heading { background-color: #2a2a2a; color: #eee; border-color: #6d6d6d; } .panel-body { background: #2e2e2e; } .panel-body a { color: #7bf; } .table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th { border-color: #6d6d6d; } /*--------------- comment ---------------*/ .ui-comment-container .ui-comment-header { background-color: #2a2a2a; color: #eee; border-color: #6d6d6d; } .ui-comment-container { background-color: #2e2e2e; border-color: #6d6d6d; } .ui-comment-container .ui-comments-container .ui-comment .comment-author { color: #eee; } .ui-comment-container .ui-comments-container .ui-comment .timestamp { color: #aaa; } .ui-comment-container .ui-comments-container .ui-comment .comment-content { color: #eee; } .ui-comment-container .ui-comments-container .ui-comment .comment-menu { color: #eee; } .ui-comment-container .ui-comments-container .ui-comment .comment-menu .comment-dropdown-menu { background: #222; color: #eee; border-color: #555; } .ui-comment-container .ui-comments-container .ui-comment .comment-menu .comment-dropdown-menu>div:hover { background-color: #555555; color: #eee; } .ui-comment-container .ui-comments-container .ui-comment .comment-menu:hover, .ui-comment-container .ui-comments-container .ui-comment .comment-menu:active, .ui-comment-container .ui-comments-container .ui-comment .comment-menu.active { background-color: #737373; color: #eee; } .ui-comment-container .ui-comment-input-container { background-color: #3c3c3c; } .ui-comment-container textarea { background-color: #3e4045; color: #eee; border: 1px solid #6d6d6d; } .ui-comment-container textarea::placeholder, .ui-comment-container textarea::-webkit-input-placeholder, .ui-comment-container textarea:-moz-placeholder, .ui-comment-container textarea::-moz-placeholder, .ui-comment-container textarea:-ms-input-placeholder { color: #eee; } @keyframes highlight { 0% { background-color: #3c3c3c; } 30% { background-color: #3c3c3c; } 100% { background-color: transparent; } } /*--------------- template ---------------*/ .template-content .modal-header { background: #2a2a2a; } .template-content .close { color: #fff; } .template-content .modal-title { color: #eee; } .template-content .ui-templates-container { border-color: #6d6d6d; } .ui-templates-container .ui-create-template-btn { background: #446fab; color: #fff; } .ui-template-list-filter .ui-template-list-filter-label, .ui-template-list-filter .ui-template-list-filter-label:hover { color: #eee; } .ui-template-list .list-group-item.active { background: #4d4d4d; } .ui-template-list .list-group-item.active:focus { background: #4d4d4d !important; } .list-group-item.active, .list-group-item.active:focus, .list-group-item.active:hover { color: #eee; } .ui-template-list .list-group-item .list-group-item-heading { color: #eee; } .ui-template-list .list-group-item.active .list-group-item-heading { color: #eee; } .ui-template-list .list-group-item:hover { background: #4d4d4d !important; } .ui-template-item-menu { color: #eee !important; } .ui-template-list .list-group-item { color: #fff; } .ui-template-list .list-group-item .dropdown-container.open { background-color: #2a2a2a; } .ui-template-list .list-group-item .dropdown-container:hover { background-color: #2a2a2a !important; } .template-menu .more-template { border-color: #6d6d6d; } .template-menu .more-template:hover { color: #eee; border-color: #6d6d6d; } /*--------------- code mirror ---------------*/ .modal-content { background: #1f2226; } .modal-header { border-bottom: 1px solid #46484f; } .modal-footer { border-top: 1px solid #46484f; } a.list-group-item { background: #1f2226; color: #ddd; border: 1px solid #46484f; } a.list-group-item .list-group-item-heading { color: #ddd; } a.list-group-item:focus, a.list-group-item:hover { background: #434651; color: #ddd; } button.close { color: #ddd; opacity: .5; } .close:focus, .close:hover { color: #fff; opacity: .8; } .CodeMirror { background: #1f2226; } .CodeMirror-gutters { background: #1f2226; border-right: 1px solid rgba(204, 217, 255, 0.1); } .cm-s-default .cm-comment { color: #888; } .cm-s-default .cm-quote { color: #ddd; } .cm-s-default .cm-header { color: #ffa653; } .cm-s-default .cm-link { color: #b0ee83; } .cm-s-default .cm-string, .cm-s-default .cm-variable-2 { color: #7bf; } .cm-s-default .cm-def { color: #c678dd; } .cm-s-default .cm-number, .cm-s-default .cm-attribute, .cm-s-default .cm-qualifier, .cm-s-default .cm-plus, .cm-s-default .cm-atom { color: #eda35e; } .cm-s-default .cm-property, .cm-s-default .cm-variable, .cm-s-default .cm-variable-3, .cm-s-default .cm-operator, .cm-s-default .cm-bracket { color: #f76e79; } .cm-s-default .cm-keyword, .cm-s-default .cm-builtin, .cm-s-default .cm-tag { color: #98c379; } .modal-title { color: #ccc; } .modal-body { color: #ccc !important; } div[contenteditable]:empty:not(:focus):before { color: #aaa; } .CodeMirror pre { color: #ddd; } .CodeMirror pre span[style^="background-color: rgb(221, 251, 230)"] { background-color: #288c27 !important; } .CodeMirror pre span[style^="background-color: rgb(249, 215, 220)"] { background-color: #a52721 !important; } /*------- code highlight: Visual Stutdio Code theme for highlight.js -------*/ .hljs { background: #1E1E1E; color: #DCDCDC; } .hljs-keyword, .hljs-literal, .hljs-symbol, .hljs-name { color: #569CD6; } .hljs-link { color: #569CD6; text-decoration: underline; } .hljs-built_in, .hljs-type { color: #4EC9B0; } .hljs-number, .hljs-class { color: #B8D7A3; } .hljs-string, .hljs-meta-string { color: #D69D85; } .hljs-regexp, .hljs-template-tag { color: #d16969; } .hljs-title { color: #dcdcaa; } .hljs-subst, .hljs-function, .hljs-formula { color: #DCDCDC; } .hljs-comment, .hljs-quote { color: #57A64A; } .hljs-doctag { color: #608B4E; } .hljs-meta, .hljs-meta-keyword, .hljs-tag { color: #9B9B9B; } .hljs-variable, .hljs-template-variable { color: #BD63C5; } .hljs-params, .hljs-attr, .hljs-attribute, .hljs-builtin-name { color: #9CDCFE; } .hljs-section { color: gold; } .hljs-emphasis { font-style: italic; } .hljs-strong { font-weight: bold; } /* .hljs-code { font-family:'Monospace'; } */ .hljs-bullet, .hljs-selector-tag, .hljs-selector-id, .hljs-selector-class, .hljs-selector-attr, .hljs-selector-pseudo { color: #D7BA7D; } .hljs-addition { background-color: #155a36; color: #dfdfdf; display: inline-block; width: 100%; } .hljs-deletion { background-color: #872e2e; color: #dfdfdf; display: inline-block; width: 100%; } /*---------- code highlight: Visual Stutdio Code theme for Prism.js ----------*/ code[class*="language-"], pre[class*="language-"] { color: #DCDCDC; } :not(pre)>code[class*="language-"], pre[class*="language-"] { background: #1E1E1E; } .token.comment, .token.block-comment, .token.prolog, .token.cdata { color: #57A64A; } .token.doctype, .token.punctuation { color: #9B9B9B; } .token.tag, .token.entity { color: #569CD6; } .token.attr-name, .token.namespace, .token.deleted, .token.property, .token.builtin { color: #9CDCFE; } .token.function, .token.function-name { color: #dcdcaa; } .token.boolean, .token.keyword, .token.important { color: #569CD6; } .token.number { color: #B8D7A3; } .token.class-name, .token.constant { color: #4EC9B0; } .token.symbol { color: #f8c555; } .token.rule { color: #c586c0; } .token.selector { color: #D7BA7D; } .token.atrule { color: #cc99cd; } .token.string, .token.attr-value { color: #D69D85; } .token.char { color: #7ec699; } .token.variable { color: #BD63C5; } .token.regex { color: #d16969; } .token.operator { color: #DCDCDC; background: transparent; } .token.url { color: #67cdcc; } .token.important, .token.bold { font-weight: bold; } .token.italic { font-style: italic; } .token.entity { cursor: help; } .token.inserted { color: green; } /*---------- code highlight: dark theme for Gist ----------*/ .gist .gist-file { border: 1px solid #555; } .gist .gist-data { background-color: #1e1e1e; border-bottom: 1px solid #555; } .gist .gist-meta { background-color: #424a55; color: #eee; } .gist .gist-meta a { color: #eee; } .gist .highlight { color: #eee; background-color: #1e1e1e; } .gist .blob-num { color: #afafaf; } .gist .blob-code-inner { color: #dfdfdf; } .pl-mb { color: #fff !important; } .pl-c { color: #57A64A !important; } /* comment */ .pl-ent { color: #569CD6 !important; } /* entity */ .pl-e { color: #9CDCFE !important; } .pl-en { color: #4EC9B0 !important; } /* entity attribute */ .pl-smi { color: #9CDCFE !important; } .pl-k { color: #569cd6 !important; } .pl-c1, .pl-s .pl-v { color: #4EC9B0 !important; } .pl-pds, .pl-s, .pl-s .pl-pse .pl-s1, .pl-sr, .pl-sr .pl-cce, .pl-sr .pl-sra, .pl-sr .pl-sre, .pl-s .pl-s1 { color: #D69D85 !important; } .pl-s .pl-s1 .pl-pse { color: #c5dbff !important; } /* strings */ .diff-table .pl-c, .diff-table .pl-ent, .diff-table .pl-e, .diff-table .pl-en, .diff-table .pl-pds, .diff-table .pl-s, .diff-table .pl-s .pl-s1, .diff-table .pl-s .pl-pse .pl-s1, .diff-table .pl-sr, .diff-table .pl-sr .pl-cce, .diff-table .pl-sr .pl-sra, .diff-table .pl-sr .pl-sre, .diff-table .pl-k, .diff-table .pl-smi, .diff-table .pl-c1, .diff-table .pl-v { color: #eee !important; } </style>