Compare commits
7 Commits
table-of-c
...
a3c299b057
| Author | SHA1 | Date | |
|---|---|---|---|
| a3c299b057 | |||
| 12aedfce92 | |||
| 65645346a2 | |||
| cb65e89e53 | |||
| 6a2fec8ef4 | |||
| aa59c90810 | |||
| 2b317930a0 |
99
code/typesafe-interpreter/TypesafeIntrV2.idr
Normal file
@@ -0,0 +1,99 @@
|
||||
data ExprType
|
||||
= IntType
|
||||
| BoolType
|
||||
| StringType
|
||||
|
||||
repr : ExprType -> Type
|
||||
repr IntType = Int
|
||||
repr BoolType = Bool
|
||||
repr StringType = String
|
||||
|
||||
intBoolImpossible : IntType = BoolType -> Void
|
||||
intBoolImpossible Refl impossible
|
||||
|
||||
intStringImpossible : IntType = StringType -> Void
|
||||
intStringImpossible Refl impossible
|
||||
|
||||
boolStringImpossible : BoolType = StringType -> Void
|
||||
boolStringImpossible Refl impossible
|
||||
|
||||
decEq : (a : ExprType) -> (b : ExprType) -> Dec (a = b)
|
||||
decEq {a = IntType} {b = IntType} = Yes Refl
|
||||
decEq {a = BoolType} {b = BoolType} = Yes Refl
|
||||
decEq {a = StringType} {b = StringType} = Yes Refl
|
||||
decEq {a = IntType} {b = BoolType} = No intBoolImpossible
|
||||
decEq {a = BoolType} {b = IntType} = No $ intBoolImpossible . sym
|
||||
decEq {a = IntType} {b = StringType} = No intStringImpossible
|
||||
decEq {a = StringType} {b = IntType} = No $ intStringImpossible . sym
|
||||
decEq {a = BoolType} {b = StringType} = No boolStringImpossible
|
||||
decEq {a = StringType} {b = BoolType} = No $ boolStringImpossible . sym
|
||||
|
||||
data Op
|
||||
= Add
|
||||
| Subtract
|
||||
| Multiply
|
||||
| Divide
|
||||
|
||||
data Expr
|
||||
= IntLit Int
|
||||
| BoolLit Bool
|
||||
| StringLit String
|
||||
| BinOp Op Expr Expr
|
||||
| IfElse Expr Expr Expr
|
||||
|
||||
data SafeExpr : ExprType -> Type where
|
||||
IntLiteral : Int -> SafeExpr IntType
|
||||
BoolLiteral : Bool -> SafeExpr BoolType
|
||||
StringLiteral : String -> SafeExpr StringType
|
||||
BinOperation : (repr a -> repr b -> repr c) -> SafeExpr a -> SafeExpr b -> SafeExpr c
|
||||
IfThenElse : { t : ExprType } -> SafeExpr BoolType -> SafeExpr t -> SafeExpr t -> SafeExpr t
|
||||
|
||||
typecheckOp : Op -> (a : ExprType) -> (b : ExprType) -> Either String (c : ExprType ** repr a -> repr b -> repr c)
|
||||
typecheckOp Add IntType IntType = Right (IntType ** (+))
|
||||
typecheckOp Subtract IntType IntType = Right (IntType ** (-))
|
||||
typecheckOp Multiply IntType IntType = Right (IntType ** (*))
|
||||
typecheckOp Divide IntType IntType = Right (IntType ** div)
|
||||
typecheckOp _ _ _ = Left "Invalid binary operator application"
|
||||
|
||||
requireBool : (n : ExprType ** SafeExpr n) -> Either String (SafeExpr BoolType)
|
||||
requireBool (BoolType ** e) = Right e
|
||||
requireBool _ = Left "Not a boolean."
|
||||
|
||||
typecheck : Expr -> Either String (n : ExprType ** SafeExpr n)
|
||||
typecheck (IntLit i) = Right (_ ** IntLiteral i)
|
||||
typecheck (BoolLit b) = Right (_ ** BoolLiteral b)
|
||||
typecheck (StringLit s) = Right (_ ** StringLiteral s)
|
||||
typecheck (BinOp o l r) = do
|
||||
(lt ** le) <- typecheck l
|
||||
(rt ** re) <- typecheck r
|
||||
(ot ** f) <- typecheckOp o lt rt
|
||||
pure (_ ** BinOperation f le re)
|
||||
typecheck (IfElse c t e) =
|
||||
do
|
||||
ce <- typecheck c >>= requireBool
|
||||
(tt ** te) <- typecheck t
|
||||
(et ** ee) <- typecheck e
|
||||
case decEq tt et of
|
||||
Yes p => pure (_ ** IfThenElse ce (replace p te) ee)
|
||||
No _ => Left "Incompatible branch types."
|
||||
|
||||
eval : SafeExpr t -> repr t
|
||||
eval (IntLiteral i) = i
|
||||
eval (BoolLiteral b) = b
|
||||
eval (StringLiteral s) = s
|
||||
eval (BinOperation f l r) = f (eval l) (eval r)
|
||||
eval (IfThenElse c t e) = if (eval c) then (eval t) else (eval e)
|
||||
|
||||
resultStr : {t : ExprType} -> repr t -> String
|
||||
resultStr {t=IntType} i = show i
|
||||
resultStr {t=BoolType} b = show b
|
||||
resultStr {t=StringType} s = show s
|
||||
|
||||
tryEval : Expr -> String
|
||||
tryEval ex =
|
||||
case typecheck ex of
|
||||
Left err => "Type error: " ++ err
|
||||
Right (t ** e) => resultStr $ eval {t} e
|
||||
|
||||
main : IO ()
|
||||
main = putStrLn $ tryEval $ BinOp Add (IfElse (BoolLit True) (IntLit 6) (IntLit 7)) (BinOp Multiply (IntLit 160) (IntLit 2))
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: About
|
||||
---
|
||||
I'm Daniel, a Computer Science student currently in my third (and final) undergraduate year at Oregon State University.
|
||||
Due my initial interest in calculators and compilers, I got involved in the Programming Language Theory research
|
||||
I'm Daniel, a Computer Science student currently working towards my Master's Degree at Oregon State University.
|
||||
Due to my initial interest in calculators and compilers, I got involved in the Programming Language Theory research
|
||||
group, gaining same experience in formal verification, domain specific language, and explainable computing.
|
||||
|
||||
For work, school, and hobby projects, I use a variety of programming languages, most commonly C/C++,
|
||||
|
||||
283
content/blog/backend_math_rendering.md
Normal file
@@ -0,0 +1,283 @@
|
||||
---
|
||||
title: Rendering Mathematics On The Back End
|
||||
date: 2020-07-15T15:27:19-07:00
|
||||
draft: true
|
||||
tags: ["Website", "Nix", "Ruby", "KaTeX", "Hugo"]
|
||||
---
|
||||
|
||||
Due to something of a streak of bad luck when it came to computers, I spent a
|
||||
significant amount of time using a Linux-based Chromebook, and then a
|
||||
Pinebook Pro. It was, in some way, enlightening. The things that I used to take
|
||||
for granted with a 'powerful' machine now became a rare luxury: StackOverflow,
|
||||
and other relatively static websites, took upwards of ten seconds to finish
|
||||
loading. On Slack, each of my keypresses could take longer than 500ms to
|
||||
appear on the screen, and sometimes, it would take several seconds. Some
|
||||
websites would present me with a white screen, and remain that way for much
|
||||
longer than I had time to wait. It was awful.
|
||||
|
||||
At one point, I installed uMatrix, and made it the default policy to block
|
||||
all JavaScript. For the most part, this worked well. Of course, I had to
|
||||
enable JavaScript for applications that needed to be interactive, like
|
||||
Slack, and Discord. But for the most part, I was able to browse the majority
|
||||
of the websites I normally browse. This went on until I started working
|
||||
on the [compiler series]({{< relref "00_compiler_intro.md" >}}) again,
|
||||
and discovered that the LaTeX math on my page, which was required
|
||||
for displaying things like inference rules, didn't work without
|
||||
JavaScript. I was left with two options:
|
||||
|
||||
* Allow JavaScript, and continue using MathJax to render my math.
|
||||
* Make it so that the mathematics is rendered on the back end.
|
||||
|
||||
I've [previously written about math rendering]({{< relref "math_rendering_is_wrong.md" >}}),
|
||||
and made the observation that MathJax's output for LaTeX is __identical__
|
||||
on every computer. From the MathJax 2.6 change log:
|
||||
|
||||
> _Improved CommonHTML output_. The CommonHTML output now provides the same layout quality and MathML support as the HTML-CSS and SVG output. It is on average 40% faster than the other outputs and the markup it produces are identical on all browsers and thus can also be pre-generated on the server via MathJax-node.
|
||||
|
||||
It seems absurd, then, to offload this kind of work into the users, to
|
||||
be done over and over again. As should be clear from the title of
|
||||
this post, this made me settle for the second option: it was
|
||||
__obviously within reach__, especially for a statically-generated website
|
||||
like mine, to render math on the backend.
|
||||
|
||||
I settled on the following architecture:
|
||||
|
||||
* As before, I would generate my pages using Hugo.
|
||||
* I would use the KaTeX NPM package to render math.
|
||||
* To build the website no matter what system I was on, I would use Nix.
|
||||
|
||||
It so happens that Nix isn't really required for using my approach in general.
|
||||
I will give my setup here, but feel free to skip ahead.
|
||||
|
||||
### Setting Up A Nix Build
|
||||
My `default.nix` file looks like this:
|
||||
|
||||
```Nix {linenos=table}
|
||||
{ stdenv, hugo, fetchgit, pkgs, nodejs, ruby }:
|
||||
|
||||
let
|
||||
url = "https://dev.danilafe.com/Web-Projects/blog-static.git";
|
||||
rev = "<commit>";
|
||||
sha256 = "<hash>";
|
||||
requiredPackages = import ./required-packages.nix {
|
||||
inherit pkgs nodejs;
|
||||
};
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
name = "blog-static";
|
||||
version = rev;
|
||||
src = fetchgit {
|
||||
inherit url rev sha256;
|
||||
};
|
||||
builder = ./builder.sh;
|
||||
converter = ./convert.rb;
|
||||
buildInputs = [
|
||||
hugo
|
||||
requiredPackages.katex
|
||||
(ruby.withPackages (ps: [ ps.nokogiri ]))
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
I'm using `node2nix` to generate the `required-packages.nix` file, which allows me,
|
||||
even from a sandboxed Nix build, to download and install `npm` packages. This is needed
|
||||
so that I have access to the `katex` binary at build time. I fed the following JSON file
|
||||
to `node2nix`:
|
||||
|
||||
```JSON {linenos=table}
|
||||
[
|
||||
"katex"
|
||||
]
|
||||
```
|
||||
|
||||
The Ruby script I wrote for this (more on that soon) required the `nokigiri` gem, which
|
||||
I used for traversing the HTML generated for my site. Hugo was obviously required to
|
||||
generate the HTML.
|
||||
|
||||
### Converting LaTeX To HTML
|
||||
After my first post complaining about the state of mathematics on the web, I received
|
||||
the following email (which the author allowed me to share):
|
||||
|
||||
> Sorry for having a random stranger email you, but in your blog post
|
||||
[(link)](https://danilafe.com/blog/math_rendering_is_wrong) you seem to focus on MathJax's
|
||||
difficulty in rendering things server-side, while quietly ignoring that KaTeX's front
|
||||
page advertises server-side rendering. Their documentation [(link)](https://katex.org/docs/options.html)
|
||||
even shows (at least as of the time this email was sent) that it renders both HTML
|
||||
(to be arranged nicely with their CSS) for visuals and MathML for accessibility.
|
||||
|
||||
This is a great point, and KaTeX is indeed usable for server-side rendering. But I've
|
||||
seen few people who do actually use it. Unfortunately, as I pointed out in my previous post on the subject,
|
||||
few tools remain that provide the software that actually takes your HTML page and substitutes
|
||||
LaTeX for math.
|
||||
|
||||
> [In MathJax,] The bigger issue, though, was that the `page2html`
|
||||
program, which rendered all the mathematics in a single HTML page,
|
||||
was gone. I found `tex2html` and `text2htmlcss`, which could only
|
||||
render equations without the surrounding HTML. I also found `mjpage`,
|
||||
which replaced mathematical expressions in a page with their SVG forms.
|
||||
|
||||
This is still the case, in both MathJax and KaTeX. The ability
|
||||
to render math in one step is the main selling point of front-end LaTeX renderers:
|
||||
all you have to do is drop in a file from a CDN, and voila, you have your
|
||||
math. There are no such easy answers for back-end rendering. I decided
|
||||
to write my own Ruby script to get the job done. From this script, I
|
||||
would call the `katex` command-line program, which would perform
|
||||
the heavy lifting of rendering the mathematics.
|
||||
|
||||
There are two types of math on my website: inline math and display math.
|
||||
On the command line ([here are the docs](https://katex.org/docs/cli.html)),
|
||||
the distinction is made using the `--display-mode` argument. So, the general algorithm
|
||||
is to replace the code inside the `$$...$$` with their display-rendered version,
|
||||
and the code inside the `\(...\)` with the inline-rendered version. I came up with
|
||||
the following Ruby function:
|
||||
|
||||
```Ruby {linenos=table}
|
||||
def render_cached(cache, command, string, render_comment = nil)
|
||||
cache.fetch(string) do |new|
|
||||
puts " Rendering #{render_comment || new}"
|
||||
cache[string] = Open3.popen3(command) do |i, o, e, t|
|
||||
i.write new
|
||||
i.close
|
||||
o.read.force_encoding(Encoding::UTF_8).strip
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Here, the `cache` argument is used to prevent re-running the `katex` command
|
||||
on an equation that was already rendered before (the output is the same, after all).
|
||||
The `command` is the specific shell command that we want to invoke; this would
|
||||
be either `katex` or `katex -d`. The `string` is the math equation to render,
|
||||
and the `render_comment` is the string to print to the console instead of the equation
|
||||
(so that long, display math equations are not printed out to standard out).
|
||||
|
||||
Then, given a substring of the HTML file, we use regular expressions
|
||||
to find the `\(...\)` and `$$...$$`s, and use the `render_cached` method
|
||||
on the LaTeX code inside.
|
||||
|
||||
```Ruby {linenos=table}
|
||||
def perform_katex_sub(inline_cache, display_cache, content)
|
||||
rendered = content.gsub /\\\(((?:[^\\]|\\[^\)])*)\\\)/ do |match|
|
||||
render_cached(inline_cache, "katex", $~[1])
|
||||
end
|
||||
rendered = rendered.gsub /\$\$((?:[^\$]|$[^\$])*)\$\$/ do |match|
|
||||
render_cached(display_cache, "katex -d", $~[1], "display")
|
||||
end
|
||||
return rendered
|
||||
end
|
||||
```
|
||||
|
||||
There's a bit of a trick to the final layer of this script. We want to be
|
||||
really careful about where we replace LaTeX, and where we don't. In
|
||||
particular, we _don't_ want to go into the `code` tags. Otherwise,
|
||||
it wouldn't be possible to talk about LaTeX code! Thus, we can't just
|
||||
search-and-replace over the entire HTML document; we need to be context
|
||||
aware. This is where `nokigiri` comes in. We parse the HTML, and iterate
|
||||
over all of the 'text' nodes, calling `perform_katex_sub` on all
|
||||
of those that _aren't_ inside code tags.
|
||||
|
||||
Fortunately, this is pretty easy to specify thanks to something called XPath.
|
||||
This was my first time encountering it, but it seems extremely useful: it's
|
||||
a sort of language for selecting XML nodes. First, you provide an 'axis',
|
||||
which is used to specify the positions of the nodes you want to look at
|
||||
relative to the root node. The axis `/` looks at the immediate children
|
||||
(this would be the `html` tag in a properly formatted document, I would imagine).
|
||||
The axis `//` looks at all the transitive children. That is, it will look at the
|
||||
children of the root, then its children, and so on. There's also the `self` axis,
|
||||
which looks at the node itself.
|
||||
|
||||
After you provide an axis, you need to specify the type of node that you want to
|
||||
select. We can write `code`, for instance, to pick only the `<code>....</code>` tags
|
||||
from the axis we've chosen. We can also use `*` to select any node, and we can
|
||||
use `text()` to select text nodes, such as the `Hello` inside of `<b>Hello</b>`.
|
||||
|
||||
We can also apply some more conditions to the nodes we pick using `[]`.
|
||||
For us, the relevant feature here is `not(...)`, which allows us to
|
||||
select nodes that do __not__ match a particular condition. This is all
|
||||
we need to know.
|
||||
|
||||
We write:
|
||||
|
||||
* `//`, starting to search for nodes everywhere, not just the root of the document.
|
||||
* `*`, to match _any_ node. We want to replace math inside of `div`s, `span`s, `nav`s,
|
||||
all of the `h`s, and so on.
|
||||
* `[not(self::code)]`, cutting out all the `code` tags.
|
||||
* `/`, now selecting the nodes that are immediate descendants of the nodes we've selected.
|
||||
* `text()`, giving us the text contents of all the nodes we've selected.
|
||||
|
||||
All in all:
|
||||
|
||||
```
|
||||
//*[not(self::code)]/text()
|
||||
```
|
||||
|
||||
Finally, we use this XPath from `nokigiri`:
|
||||
|
||||
```Ruby {linenos=table}
|
||||
files = ARGV[0..-1]
|
||||
inline_cache, display_cache = {}, {}
|
||||
|
||||
files.each do |file|
|
||||
puts "Rendering file: #{file}"
|
||||
document = Nokogiri::HTML.parse(File.open(file))
|
||||
document.search('//*[not(self::code)]/text()').each do |t|
|
||||
t.replace(perform_katex_sub(inline_cache, display_cache, t.content))
|
||||
end
|
||||
File.write(file, document.to_html)
|
||||
end
|
||||
```
|
||||
|
||||
I named this script `convert.rb`; it's used from inside of the Nix expression
|
||||
and its builder, which we will cover below.
|
||||
|
||||
### Tying it All Together
|
||||
Finally, I wanted an end-to-end script to generate HTML pages and render the LaTeX in them.
|
||||
I used Nix for this, but the below script will largely be compatible with a non-Nix system.
|
||||
I came up with the following, commenting on Nix-specific commands:
|
||||
|
||||
```Bash {linenos=table}
|
||||
source $stdenv/setup # Nix-specific; set up paths.
|
||||
|
||||
# Build site with Hugo
|
||||
# The cp is Nix-specific; it copies the blog source into the current directory.
|
||||
cp -r $src/* .
|
||||
hugo --baseUrl="https://danilafe.com"
|
||||
|
||||
# Render math in HTML and XML files.
|
||||
# $converter is Nix-specific; you can just use convert.rb.
|
||||
find public/ -regex "public/.*\.html" | xargs ruby $converter
|
||||
|
||||
# Output result
|
||||
# $out is Nix-specific; you can replace it with your destination folder.
|
||||
mkdir $out
|
||||
cp -r public/* $out/
|
||||
```
|
||||
|
||||
This is it! Using the two scripts, `convert.rb` and `builder.sh`, I
|
||||
was able to generate my blog with the math rendered on the back-end.
|
||||
Please note, though, that I had to add the KaTeX CSS to my website's
|
||||
`<head>`.
|
||||
|
||||
### Caveats
|
||||
The main caveat of my approach is performance. For every piece of
|
||||
mathematics that I render, I invoke the `katex` command. This incurs
|
||||
the penalty of Node's startup time, every time, and makes my approach
|
||||
take a few dozen seconds to run on my relatively small site. The
|
||||
better approach would be to use a NodeJS script, rather than a Ruby one,
|
||||
to perform the conversion. KaTeX also provides an API, so such a NodeJS
|
||||
script can find the files, parse the HTML, and perform the substitutions.
|
||||
I did quite like using `nokigiri` here, though, and I hope that an equivalently
|
||||
pleasant solution exists in JavaScript.
|
||||
|
||||
Re-rendering the whole website is also pretty wasteful. I rarely change the
|
||||
mathematics on more than one page at a time, but every time I do so, I have
|
||||
to re-run the script, and therefore re-render every page. This makes sense
|
||||
for me, since I use Nix, and my builds are pretty much always performed
|
||||
from scratch. On the other hand, for others, this may not be the best solution.
|
||||
|
||||
### Conclusion
|
||||
With the removal of MathJax from my site, it is now completely JavaScript free,
|
||||
and contains virtually the same HTML that it did beforehand. This, I hope,
|
||||
makes it work better on devices where computational power is more limited.
|
||||
I also hope that it illustrates a general principle - it's very possible,
|
||||
and plausible, to render LaTeX on the back-end for a static site.
|
||||
BIN
content/blog/dell_is_horrible/brokenkey.jpg
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
content/blog/dell_is_horrible/brokenlcd.jpg
Normal file
|
After Width: | Height: | Size: 476 KiB |
BIN
content/blog/dell_is_horrible/dm_1.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
content/blog/dell_is_horrible/dm_2.png
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
content/blog/dell_is_horrible/dm_3.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
content/blog/dell_is_horrible/dm_4.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
content/blog/dell_is_horrible/dm_5.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
384
content/blog/dell_is_horrible/index.md
Normal file
@@ -0,0 +1,384 @@
|
||||
---
|
||||
title: DELL Is A Horrible Company And You Should Avoid Them At All Costs
|
||||
date: 2020-07-17T16:23:34-07:00
|
||||
draft: true
|
||||
tags: ["Electronics"]
|
||||
---
|
||||
|
||||
I really do not want this to be a consumer electronics blog. Such things
|
||||
aren't interesting to me, and nor do I have much knowledge
|
||||
about them. However, sometimes, ripples from these areas make their way
|
||||
into my life, and this is one such instance. Let me tell you
|
||||
{{< sidenote "right" "source-note" "a story" >}}
|
||||
I originally wrote about this in
|
||||
<a href="https://www.dell.com/community/XPS/Ridiculously-Bad-Support-Experience/td-p/7554383">a thread on DELL's support website</a>. Some of this post is
|
||||
going to be adapted from the support website, but some things have happened
|
||||
since. You will probably notice the change between the terse language I used
|
||||
in the original post and the fresh text that I'm writing now.
|
||||
{{< /sidenote >}} of
|
||||
my experience with DELL and their XPS 2-in-1 laptop, which has gone on since
|
||||
around January of 2020, and is still going at the time of writing, in July
|
||||
2020, half a year later.
|
||||
|
||||
I was, until recently, an undergraduate student in Computer Science. I will
|
||||
soon be starting my Masters in Computer Science, too. I say this to make one
|
||||
thing clear: I need a computer. Not only is it a necessity for my major,
|
||||
but the majority of my hobbies -- including this blog -- are digital, too.
|
||||
Since my university is a couple of hours from my home, I travel back and forth
|
||||
a lot. I also have a cozy little spot in the
|
||||
{{< sidenote "right" "offices-note" "graduate student offices" >}}
|
||||
They're a bunch of cubicles in a keycard-protected room, really. Nothing fancy.
|
||||
{{< /sidenote >}}at my university, but travel by bus, so I find myself spending
|
||||
roughly equal portions of my work time at home and 'elsewhere'. A laptop
|
||||
as my primary machine, I thought, made sense. But it had to be a decent one.
|
||||
Persuaded by one of my instructors, who stressed the importance of vision and
|
||||
a decent screen, I settled on a DELL XPS, which at the time came with a 4k
|
||||
display.
|
||||
|
||||
As is commonplace, things went great at first. The screen _was_ really nice,
|
||||
all of my code compiled swiftly, and even the games I occasionally played ran
|
||||
at a solid 60fps. I was happy with my purchase.
|
||||
|
||||
There was one hiccup before things went really downhill, a sort of
|
||||
foreshadowing of things to come. My trackpad didn't work at peculiar times.
|
||||
|
||||
### Prologue: Trackpad Hiccups
|
||||
While working, booted into Linux, I noticed that my trackpad was having some
|
||||
trouble. It was stuttering, and occasionally wouldn't work at all for seconds
|
||||
at a time. I assumed that this was a problem with the trackpad drivers on
|
||||
Linux, or perhaps the whole system was freezing up. I rebooted, and the
|
||||
problem went away.
|
||||
|
||||
Until it came back.
|
||||
|
||||
A few days later, my trackpad was freezing virtually every minute.
|
||||
It was strange, but fortunately, I'm used to a keyboard-based workflow, and
|
||||
the malfunctions did not affect me too much. It was just a little troubling.
|
||||
What soon made it more troubling, was that I noticed this exact same issue
|
||||
occurring on Windows. To me, this meant one dreadful thing: it was a hardware
|
||||
issue.
|
||||
|
||||
I poked and prodded for a little bit, and finally discovered the cause:
|
||||
whenever I put my hand on the left palmrest, the trackpad would reliably stop
|
||||
working. Knowing what the issue was, I called DELL. I spoke to a guy on the
|
||||
other end, who had me run through diagnostics, driver updates, and BIOS
|
||||
settings (I imagined this was procedure, so I didn't mind doing the extra
|
||||
work to make the other guy's job easier). Finally, he scheduled a repair
|
||||
appointment. A technician came into my house, took off the laptop cover,
|
||||
and said something along the lines of:
|
||||
|
||||
> Now look. They gave me a whole new motherboard and case to replace yours,
|
||||
but in my personal opinion, this is a bad idea. Things are bound to break
|
||||
when you do this. See how the replacement case has an insulating piece
|
||||
of fabric under the left palmrest, and yours doesn't? Why don't we rip
|
||||
the fabric off the replacement case, and tape it in place on your machine,
|
||||
without any reassembly?
|
||||
|
||||
This man was wiser than any of the other DELL technicians, I now understand.
|
||||
The repair went without a hitch. He grilled me for going to college instead of
|
||||
just picking up a trade, which was cheaper and offered more job security.
|
||||
In the end, I felt a little weird about having a piece of fabric duct taped
|
||||
inside my computer, but the trackpad had no more issues ever since. All was
|
||||
well.
|
||||
|
||||
### Service Request 1: Broken D Key
|
||||
All was well, that is, until the middle of winter term. I was typing up an
|
||||
assignment for a university class. I was working as usual, when I suddenly
|
||||
noticed that the "d" key stopped working - it had to be pressed rather weird
|
||||
to register on the computer. I looked down, and discovered that the key had
|
||||
snapped in half. The top part of the key fell off shortly thereafter.
|
||||
|
||||
{{< figure src="brokenkey.jpg" caption="The broken D key shortly after the above events." >}}
|
||||
|
||||
At that point, I was more surprised than anything. I hadn't heard of something
|
||||
like this ever happening, especially under circumstances as normal as typing.
|
||||
Regardless, I contacted support, and set up a repair appointment. Things only
|
||||
went downhill from there.
|
||||
|
||||
Again, the appointment was scheduled, and only a few days later, another
|
||||
technician arrived at my house. The only way to repair the key, he said,
|
||||
was to replace the whole keyboard. They keyboard happens to be located
|
||||
underneath all the other hardware, and so, the entire laptop had to be
|
||||
disassembled and reassembled from scratch. He worked for about an hour, and
|
||||
eventually, he put the machine together. The words of the previous
|
||||
technician, who wanted to avoid doing exactly what had just been done, echoed
|
||||
in my head:
|
||||
|
||||
> Things are bound to break when you do this.
|
||||
|
||||
I asked him to test it, just to make sure everything works. Sure enough,
|
||||
not everything did work: the machine no longer had sound!
|
||||
|
||||
### Service Request 2: No sound
|
||||
During diagnostics, the laptop did not emit the "beep" it usually does. This
|
||||
was the first sign. Booting into Windows, the sound icon was crossed out in
|
||||
red, and no sound was present. Booting into Linux led to similar results.
|
||||
The microphone on the machine did not seem to work either. The service
|
||||
technician said that he didn't have the parts to repair it, told me he'd call
|
||||
it in, and left. Soon after, I got an email asking for times I'm available to
|
||||
call: I said "any time except for 1-4 pacific time". DELL support proceeded
|
||||
to call me at 3pm pacific time, when I had no service. Unable to reach me,
|
||||
they promptly notified me that they are archiving my service request.
|
||||
|
||||
This all occurred near finals week at my university, so I had to put the issue
|
||||
on hold. I had to maintain my grades, and I had to grade heaps of assignments
|
||||
from other students. Though the lack of sound was annoying, it wasn't as
|
||||
pressing as preparing for exams, so it was during spring break that I finally
|
||||
called again, and scheduled the service appointment. By then,
|
||||
{{< sidenote "right" "pandemic-note" "the pandemic was in full swing," >}}
|
||||
Just for posterity, in 2020, there had been an outbreak of COVID-19,
|
||||
a Coronavirus. Many states in the U.S., including my own, issued
|
||||
the orders for lockdown and social distancing, which meant the closing
|
||||
of schools, restaurants, and, apparently, the cessation of in-person
|
||||
repairs.
|
||||
{{< /sidenote >}}and DELL told me they'd mail me a box to put my laptop in, and
|
||||
I'd have to mail it off to their service center. Sure, I thought, that's
|
||||
fine. If it's at the service center, they won't ever "not have the required
|
||||
parts". I told the tech support person my address, he read it back to me, and
|
||||
so it was settled.
|
||||
|
||||
Until, that is, the box arrived at the wrong address.
|
||||
|
||||
I had received the machine as a gift from my family, who purchased the
|
||||
computer to arrive at their address. The box arrived at that address too,
|
||||
despite my explicit instructions to have it deliver to my current residence.
|
||||
Since my family and I live 2 hours apart, it took 4 total hours to get the box
|
||||
to me (a drive that couldn't be made right away!), and by the time I had it,
|
||||
DELL was already threatening me again with closing the service request.
|
||||
Eventually, I was able to mail the machine off, and about 5 business days
|
||||
later (business days during which I did not have a working machine, which is
|
||||
very necessary for my school and job) I received it back. I was excited to
|
||||
have the machine back, but that didn't last very long. As I was using the
|
||||
computer with Wolfram Mathematica (a rather heavy piece of software running
|
||||
under Linux), I noticed that it was discharging even while plugged in. I
|
||||
booted into Windows, and was greeted with a warning, something along the
|
||||
lines of: "you are using a slow charger. Please use the official adapter".
|
||||
But I was using the official adapter! I also tried to plug my mouse into the
|
||||
relevant USB-C port, only to discover that it did not work. I had to make
|
||||
another service requests.
|
||||
|
||||
### Service Request 3: Broken Charging Port
|
||||
This time, I made sure to tell the person on the other end of the support
|
||||
call to please send it to my address. I asked if there was anything I can do,
|
||||
or anyone I can contact, and was told "no, just mail the computer in again."
|
||||
I obliged. The box arrived at the right address this time, so I was able to
|
||||
ship it off.
|
||||
|
||||
In the "describe your issue" field on the provided form, I begged the
|
||||
technicians to send me a working machine. "Please", I wrote "Last time I got
|
||||
a machine back from support, it was still broken. I really need it for school
|
||||
and work!". 5 business days later, I received the machine back. I plugged it
|
||||
in to make sure it worked, only to find out . . . that the very same charging
|
||||
port that I requested be repaired, is still broken! It would've been funny,
|
||||
if it wasn't infuriating. How is it possible for me to receive a machine from
|
||||
repairs, without the thing I asked to repair being as much as improved?!
|
||||
|
||||
Worse, a day after I received the machine back (I was able to keep using it
|
||||
thanks to it having two USB-C ports capable of charging), the LCD suddenly
|
||||
flashed, and started flickering. Thinking it was a software glitch, I
|
||||
restarted the machine, only to discover the same flickering during the boot
|
||||
animation and menu. Not only was the charging port not repaired, but now my
|
||||
LCD was broken! (in the below picture, the screen is meant to be blue, but
|
||||
the bottom part of the display is purple and flickering).
|
||||
|
||||
{{< figure src="brokenlcd.jpg" caption="The broken LCD." >}}
|
||||
|
||||
### Service Request 4: Broken LCD
|
||||
I called in to support again, and they once again told me to ship the machine
|
||||
off. What's worse, they accused me of breaking the port myself, and told me
|
||||
this was no longer covered under basic warranty. I had to explain all over
|
||||
again that the port worked fine before the fateful day the D-key snapped. They
|
||||
told me they'd "look into it". Eventually, I received a box in the mail. I
|
||||
wasn't told I would be receiving a box, but that wasn't a big deal. I mailed
|
||||
off the machine.
|
||||
|
||||
The UPS shipping was always the most streamlined part of the process. A day
|
||||
later, I was told my machine was received intact. Another day, and I was
|
||||
informed that the technicians are starting to work on it. And then,
|
||||
a few hours later:
|
||||
|
||||
> __Current Status:__
|
||||
> The part(s) needed to repair your system are not currently in stock.
|
||||
> __What's Next:__
|
||||
> In most cases the parts are available is less than five days.
|
||||
|
||||
A few days is no big deal, and it made sense that DELL wouldn't just
|
||||
have screens lying around. So I waited. And waited. And waited. Two weeks
|
||||
later, I got a little tired of waiting, and called the repair center.
|
||||
An automated message told me:
|
||||
|
||||
> We're currently experiencing heavy call volumes. Please try again later. Goodbye.
|
||||
|
||||
And the call was dropped. This happened every time I tried to call, no matter
|
||||
the hour. The original status update -- the one that notified me about the
|
||||
part shortage -- came on May 8th, but the machine finally arrived to me
|
||||
(without prior warning) on June 2nd, almost a month later.
|
||||
|
||||
The charging port worked. Sound
|
||||
worked. The screen wasn't flickering. I was happy for the brief moments that
|
||||
my computer was loading. As soon as I started vim, though, I noticed something
|
||||
was off: the fonts looked more pixelated. The DPI settings I'd painstakingly
|
||||
tweaked were wrong. Now that I thought about it, even the GRUB menu was
|
||||
larger. My suspicion growing, I booted into Windows, and looked at the display
|
||||
settings. Noticeably fewer resolutions were listed in the drop-down menu;
|
||||
worse, the highest resolution was 1080p. After almost a month of waiting,
|
||||
DELL replaced my 4k laptop display with a 1080p one.
|
||||
|
||||
### System Replacement: Worse LCD Screen
|
||||
|
||||
I admit, I was angry. At the same time, the absurdity of it all was also
|
||||
unbearable. Was this constant loop of hardware damage, the endless number of
|
||||
support calls filled with hoarse jazz music, part of some kind of Kafkaesque
|
||||
dream? I didn't know. I was at the end of my wits as to what to do. As a last
|
||||
resort, I made a tweet from my almost-abandoned account. DELL Support's Twitter
|
||||
account quickly responded, eager as always to destroy any semblance of
|
||||
transparency by switching to private messages:
|
||||
|
||||
{{< tweet 1268064691416334344 >}}
|
||||
|
||||
I let them know my thoughts on the matter. I wanted a new machine.
|
||||
|
||||
{{< figure src="dm_1.png" caption="The first real exchange between me and DELL support." >}}
|
||||
|
||||
Of course we can proceed further. I wanted to know what kind of machine I was getting,
|
||||
though. As long as it was the same model that I originally bought,
|
||||
{{< sidenote "right" "replacement-note" "it would be better than what I have." >}}
|
||||
At least in principle, it would be. Perhaps the wear and tear on the replacement
|
||||
parts would be greater, but at least I would have, presumably, a machine
|
||||
in good condition that had the 4k screen that made me buy it in the first place.
|
||||
{{< /sidenote >}}
|
||||
Despite this, I knew that the machine I was getting was likely refurbished.
|
||||
This _had_ to mean that some of the parts would come from other, used, machines.
|
||||
This irked me, because, well, I payed for a new machine.
|
||||
|
||||
{{< figure src="dm_2.png" caption="Ah, the classic use of canned responses." >}}
|
||||
|
||||
Their use of the canned response, and their unwillingness to answer this simple
|
||||
question, was transparent. Indeed, the machine would be made of used
|
||||
parts. I still wanted to proceed. DELL requested that I sent an image of
|
||||
my machine which included its service tag, together with a piece of
|
||||
paper which included my name and email address. I obliged, and quickly got a response:
|
||||
|
||||
{{< figure src="dm_3.png" caption="If it was me who was silent for 4 days, my request would've long been cancelled. " >}}
|
||||
|
||||
Thanks, Kalpana. Try to remember that name; you will never hear it again, not in this post.
|
||||
About a week later, I get the following beauty:
|
||||
|
||||
{{< figure src="dm_4.png" caption="Excuse me? What's going on?" >}}
|
||||
|
||||
My initial request was cancelled? Why wasn't I told? What was the reason?
|
||||
What the heck was going on at DELL Support? Should I be worried?
|
||||
My question of "Why" was answered with the apt response of "Yes",
|
||||
and a message meant to pacify me. While this was going on, I ordered
|
||||
a
|
||||
{{< sidenote "right" "pinebook-note" "Pinebook Pro." >}}
|
||||
The Pinebook – a $200 machine – has, thus far, worked more reliably than any DELL product
|
||||
I've had the misfortune of owning.
|
||||
{{< /sidenote >}} It was not a replacement for the DELL machine, but rather
|
||||
the first step towards migrating my setup to a stationary computer,
|
||||
and a small, lightweight SSH device. At this point,
|
||||
there was no more faith in DELL left in my mind.
|
||||
|
||||
Soon, DELL required my attention, only to tell me that they put in
|
||||
a request to see that status of my request. How bureaucratic. Two
|
||||
more names -- Kareem and JKC -- flickered through the chats,
|
||||
also never to be seen again.
|
||||
|
||||
{{< figure src="dm_5.png" caption="Not much of a conversation, really." >}}
|
||||
|
||||
Finally, on July 9th (a month and six days after my first real message to DELL
|
||||
support), I was notified by my roommates that FedEx tried to deliver a package
|
||||
to our house, but gave up when no one came to sign for it. On one hand, this
|
||||
is great: FedEx didn't just leave my laptop on the porch. On the other hand,
|
||||
though, this was the first time I heard about receiving the machine. I got
|
||||
to the house the next day, unpacked the new computer, and tested all the things
|
||||
that had, at one point, failed. Everything seemed to work. I transfered all my
|
||||
files, wiped the old computer clean, and mailed it off. I also spent some
|
||||
time dealing with the fallout of DELL PremierColor starting on its own,
|
||||
and permanently altering the color profile of my display. I don't have the
|
||||
special, physical calibration device, and therefore still suspect that my
|
||||
screen is somewhat green.
|
||||
|
||||
Today, I discovered that the microphone of the replacement machine didn't work.
|
||||
|
||||
### Am I The Problem?
|
||||
When the mysterious FedEx package arrived at my door on July 9th, I did some
|
||||
digging to verify my suspicion that it was from DELL. I discovered their
|
||||
HQ in Lebanon, TN. This gave me an opportunity to
|
||||
{{< sidenote "right" "reviews-note" "see" >}}
|
||||
See, of course, modulo whatever bias arises when only those who feel strongly leave reviews.
|
||||
{{< /sidenote >}} whether or not I was alone in this situation. I was genuinely
|
||||
worried that I was suffering from the technical variant of
|
||||
[Munchausen Syndrome](https://www.webmd.com/mental-health/munchausen-syndrome#1),
|
||||
and that I was compulsively breaking my electronics. These worries were
|
||||
dispelled by the reviews on Google:
|
||||
|
||||
{{< figure src="reviews_1.png" caption="Most of the reviews are pretty terse, but the ratings convey the general idea." >}}
|
||||
|
||||
There were even some that were shockingly similar in terms of the apparent
|
||||
incompetence of the DELL technicians:
|
||||
|
||||
{{< figure src="reviews_2.png" caption="Now, now, Maggie, I wouldn't go as far as recommending Apple." >}}
|
||||
|
||||
So, this is not uncommon. This is how DELL deals with customers now. It's
|
||||
awfully tiring, really; I've been in and out of repairs continuously for
|
||||
almost half a year, now. That's 2.5% of my life at the time of writing,
|
||||
all non-stop since the D-key. And these people probably have spent considerable
|
||||
amounts of time, too.
|
||||
|
||||
### It's About the Principle
|
||||
The microphone on my machine is rather inconsequential to me. I can, and regularly do,
|
||||
teleconference from my phone (a habit that I developed thanks to DELL, since
|
||||
my computer was so often unavailable). I don't need to dictate anything. Most
|
||||
of my communication is via chat.
|
||||
|
||||
Really, compared to the other issues (keyboard, sound, charging, USB ports, the broken or low-resolution screen)
|
||||
the microphone is a benign problem. As I have now learned, things could be worse.
|
||||
|
||||
But why should the thought, _"It could be worse"_, even cross my mind
|
||||
when dealing with such a matter? Virtually every issue that has
|
||||
occurred with my computer thus far could -- should! -- have been diagnosed
|
||||
at the repair center. The 'slow charger' warning shows up in BIOS,
|
||||
so just turning the computer on while plugged in should make it obvious something
|
||||
is wrong; doubly so when the very reason that the laptop was in repairs
|
||||
in the first place was because of the faulty charger. I refuse to believe
|
||||
that screens with different resolutions have the same part identifier,
|
||||
either. How have the standards of service from DELL fallen so low?
|
||||
How come this absurd scenario plays out not just for me, but
|
||||
for others as well? It would be comforting, in a way, to think
|
||||
that I was just the 'exceptional case'. But apparently, I'm not.
|
||||
This is standard practice.
|
||||
|
||||
### Tl;DR
|
||||
Here are he problems I've had with DELL:
|
||||
|
||||
* The machine shipped, apparently, with a missing piece of insulation.
|
||||
* The "D" key on the keyboard snapped after only a few months of use.
|
||||
* While repairing the "D" key, the DELL technician broke the computer's sound and microphone.
|
||||
* While repairing the sound and microphone, the DELL technicians broke a charging port.
|
||||
* The DELL technicians failed to repair the charging port, mailing me back a machine
|
||||
exhibiting the same issues, in addition to a broken LCD screen.
|
||||
* The repair of the LCD screen took almost a month, and concluded
|
||||
with me receiving a worse quality screen than I originally had.
|
||||
* The system replacement that followed the botched LCD repair took
|
||||
over a month to go through.
|
||||
* The replaced system was made partially of used parts, which
|
||||
DELL refused to admit.
|
||||
* The microphone on the replacement system was broken.
|
||||
|
||||
### Closing Thoughts
|
||||
I will not be sending my system in again. It doesn't make sense to do so -
|
||||
after mailing my system in for repairs three times, I've measured empirically that
|
||||
the chance of failure is 100%. Every service request is a lottery, dutifully
|
||||
giving out a random prize of another broken part. I no longer wish to play;
|
||||
as any person who gambles should, I will quit while I'm ahead, and cut my losses.
|
||||
However, I hope for this story, which may be unusual in its level of detail,
|
||||
but not its content, to be seen by seen by someone. I hope to prevent
|
||||
someone out there from feeling the frustration, and anger, and peculiar amusement
|
||||
that I felt during this process. I hope for someone else to purchase a computer
|
||||
with money, and not with their sanity. A guy can hope.
|
||||
|
||||
If you're reading this, please take this as a warning. __DELL is a horrible
|
||||
company. They have the lowest standards of customer support of any
|
||||
U.S. company that I've encountered. Their technicians are largely incompetent.
|
||||
Their quality assurance is non-existent. Stay away from them.__
|
||||
BIN
content/blog/dell_is_horrible/reviews_1.png
Normal file
|
After Width: | Height: | Size: 180 KiB |
BIN
content/blog/dell_is_horrible/reviews_2.png
Normal file
|
After Width: | Height: | Size: 227 KiB |
BIN
static/Resume-Danila-Fedorin.pdf
Normal file
@@ -242,3 +242,7 @@ figure {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.twitter-tweet {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
<nav>
|
||||
<div class="container">
|
||||
<a href="/">Home</a>
|
||||
<a href="https://github.com/DanilaFe">GitHub</a>
|
||||
<a href="/about">About</a>
|
||||
<a href="https://github.com/DanilaFe">GitHub</a>
|
||||
<a href="/Resume-Danila-Fedorin.pdf">Resume</a>
|
||||
<a href="/tags">Tags</a>
|
||||
<a href="/blog">All Posts</a>
|
||||
</div>
|
||||
|
||||