Commit 7da41f7e authored by Dario Pinto's avatar Dario Pinto
Browse files

add redirections, one missing article (fr version), and a bunch of fixes for articles, urls

parent f26e5dc4
...@@ -188,13 +188,13 @@ let article_of_string post url = ...@@ -188,13 +188,13 @@ let article_of_string post url =
let authors = let authors =
let auth = get_meta_value author in let auth = get_meta_value author in
match String.split_on_char ',' auth with match String.split_on_char ',' auth with
| [ "" ] -> [ "Unspecified authors!" ] | [ "" ] -> [ "Unspecified authors." ]
| auth -> auth | auth -> auth
in in
let tags = let tags =
let tags = get_meta_value tags in let tags = get_meta_value tags in
match String.split_on_char ',' tags with match String.split_on_char ',' tags with
| [ "" ] -> [ "Unspecified tags!" ] | [ "" ] -> [ "Unspecified tags." ]
| tags -> tags | tags -> tags
in in
let category = let category =
......
title=Announcing Liquidity version 1.0 title=Announcing Liquidity version 1.0
authors=Alain Mebsout= authors=Alain Mebsout
date=2019-03-08 date=2019-03-08
category=Blockchains category=Blockchains
tags=liquidity tags=liquidity
......
...@@ -659,7 +659,7 @@ Et l’affichage dans le terminal sera bien celui voulu : ...@@ -659,7 +659,7 @@ Et l’affichage dans le terminal sera bien celui voulu :
Si le programme doit être affiché dans un terminal non ANSI il suffit simplement d’enlever la ligne `add_ansi_marking std_formatter;` : Si le programme doit être affiché dans un terminal non ANSI il suffit simplement d’enlever la ligne `add_ansi_marking std_formatter;` :
![Exemple de marquage avec la gestion des tags sémantiques par Format dans un terminal ANSI](ansi-color-try-stag.png) ![Exemple de marquage avec la gestion des tags sémantiques par Format dans un terminal ANSI](/blog/assets/img/ansi-color-try-stag.png)
On pourrait aussi faire en sorte que notre texte puisse être envoyé vers un document HTML. On pourrait aussi faire en sorte que notre texte puisse être envoyé vers un document HTML.
......
title=[Interview] Sylvain Conchon rejoint OCamlPro
authors=Aurore Dombry
date=2020-06-05
category=OCamlPro
tags=formal methods,interview,sylvain,conchon
![](/blog/assets/img/picture_sylvainconchon.jpg)
> Sylvain Conchon vient de rejoindre OCamlPro en tant que Chief Scientific Officer Méthodes Formelles. Professeur à l’Université Paris-Saclay, il travaille dans le domaine de la démonstration automatique pour la preuve de programmes et le model checking pour systèmes paramétrés. Il est aussi le co-créateur d’Alt-Ergo.
### Recherche et industrie
**Sylvain, tu fréquentes de longue date le monde industriel,
que penses-tu des interactions entre les industriels et les laboratoires
de recherche ?**
J’ai toujours trouvé très enrichissantes les interactions avec les
industriels. Pendant mes études, j’ai travaillé plusieurs années en
SSII, et je suis mes étudiants en stage ou en apprentissage dans des
sociétés technologiques ou chez de grands industriels. Je participe
également à des projets de recherche qui impliquent des industriels,et
j’ai passé quelques temps chez Intel à Portland, ce qui m’a permis de
découvrir l’industrie du hardware.
**Comment parvenir à établir des relations fructueuses entre le monde académique et les industriels ?**
C’est beaucoup une histoire de rencontre. On le voit lors des
montages de projets de recherche collaboratifs qui réunissent
académiques et industriels. Les outils issus de la recherche, quels
qu’ils soient, doivent avant tout répondre à un besoin réel des
industriels. Si c’est le cas, il faut aussi que le logiciel soit
utilisable par des ingénieurs du métier sans qu’il leur soit nécessaire
de comprendre son fonctionnement interne (par exemple, pour positionner
les 50 options nécessaires à son utilisation, interpréter ses résultats
ou ses absences de résultats!). Cela nécessite à l’évidence un travail
d’ingénierie important, tourné vers l’utilisateur final et souvent
éloigné des activités des chercheurs. Il faut donc comprendre les
problèmes et les besoins des industriels, et ensuite déterminer si les
technologies et les outils que l’on maîtrise peuvent être adaptés ou
utilisés pour réaliser un prototype qui réponde à certains de ces
besoins.
**Tu viens de rejoindre OCamlPro, quelles sont tes premières impressions ?**
Je suis heureux d’avoir rejoint une entreprise très dynamique, pleine
de gens talentueux, motivés et sympathiques, où l’on fait à la fois de
l’ingénierie de haut niveau et de la recherche de qualité !
> *“ Les outils issus de la recherche, quels qu’ils soient, doivent avant tout répondre à un besoin réel des industriels.”*
### OCaml, un langage de pointe
**Tu es connu dans la communauté OCaml, et certains de
tes étudiants sont devenus des fans d’OCaml (et de ton enseignement)…
que dis-tu à tes étudiants qui découvrent OCaml ?**
J’ai tendance à résumer en disant ceci : *« avec OCaml, vous n’apprenez pas la programmation des 10 dernières années, mais celle des 10 prochaines années »*. Cette affirmation s’est toujours vérifiée car bon nombre de traits du langage OCaml se sont retrouvés dans les langages *mainstream*, avec plusieurs années de décalage. Cela dit, mes années d’expérience dans l’enseignement de ce langage me laissent penser que quelques modifications dans sa syntaxe permettraient une approche plus aisée pour certains débutants.
**Et toi, comment as-tu découvert OCaml ?**
Pendant mes études à l’Université lors de mon projet de fin de
maîtrise : un de mes enseignants m’avait orienté vers ce langage pour
m’aider à réaliser un compilateur pour un langage de programmation
concurrente. J’ai donc découvert ce langage par moi-même, en lisant le
manuel et les exemples. Ce n’est que pendant mon DEA que j’ai découvert
les fondements théoriques de ce beau langage (sémantique, typage,
compilation).
**OCaml, un langage industriel ou pas encore ?**
Il convient de préciser la question : qu’est-ce qu’un langage
industriel ? Si c’est un langage utilisé par les industriels, alors
OCaml n’est hélas pas encore suffisamment utilisé dans l’industrie pour
être qualifié ainsi. Si la question est de savoir s’il a le niveau des
langages utilisés dans l’industrie, alors la réponse est oui, sans
hésiter. Mais peut-être la question porte-t-elle davantage sur
l’écosystème OCaml et la maturité de l’outillage: il y a sûrement des
progrès à faire pour atteindre le niveau d’un langage très répandu dans
l’industrie, mais c’est en bonne voie, en particulier grâce à des
entreprises telles qu’OCamlPro.
### Les méthodes formelles comme technique industrielle, et l’exemple du solveur Alt-Ergo
**Les méthodes formelles sont l’un des domaines d’expertise d’OCamlPro, en quoi penses-tu qu’OCaml est adapté au domaine des SMT ?**
Les outils comme les solveurs SMT sont principalement des logiciels
de manipulation symbolique des données qui permettent d’analyser, de
transformer et de raisonner sur des formules logiques. OCaml est fait
pour ce genre de traitements. Il y a aussi une partie plus «
calculatoire » dans ces outils qui nécessite une programmation fine des
structures de données ainsi qu’une gestion efficace de la mémoire. OCaml
est particulièrement adapté pour ce genre de développements, surtout
avec son ramasse-miettes (GC) extrêmement performant. Enfin, les
solveurs SMT sont des outils qui doivent avoir un grand niveau de
fiabilité car les erreurs dans ces logiciels sont difficiles à trouver
et leur présence peut être très préjudiciable. Le système de types
d’OCaml contribue à la fiabilité de ces outils.
> *“Les solveurs SMT sont aujourd’hui incontournables dans le domaine de l’ingénierie du logiciel.”*
**Peux-tu nous parler d’Alt-Ergo en quelques mots ?**
C’est un logiciel utilisé pour prouver automatiquement (sans
intervention humaine) des formules logiques, c’est-à-dire savoir si ces
formules sont vraies ou fausses. Alt-Ergo appartient à une famille de
démonstrateurs automatiques appelée SMT (pour Satisfiabilité Modulo
Théories). Il a été conçu pour être intégré dans des plate-formes de
vérification de programmes. Ces outils (comme Why3, Frama-C, Spark,…)
génèrent des formules logiques qu’il est nécessaire de prouver afin de
garantir qu’un programme est sûr. Faire la preuve de ces formules à la
main serait très fastidieux (il y a parfois plusieurs dizaines de
milliers de formules à prouver). Un solveur SMT comme Alt-Ergo est là
pour faire ce travail, de manière complètement automatique. C’est ce qui
permet à ces plateformes de vérification d’être utilisables au niveau
industriel.
**En quoi le développement d’Alt-Ergo en OCaml peut-il être un avantage par rapport aux concurrents ?**
Cela lui confère une plus grande sûreté, car un solveur SMT, comme
n’importe quel programme peut aussi avoir des bugs. La plus grande
partie d’Alt-Ergo est programmée dans un style purement fonctionnel,
c’est-à-dire uniquement avec l’utilisation de structures de données
immuables. L’un des avantages de ce style de programmation est qu’il
nous a permis de prouver formellement ses principaux composants (par
exemple, son noyau a été formalisé à l’aide de l’assistant à la preuve
Coq, ce qui serait impossible à faire dans un langage comme C++), sans
sacrifier son efficacité grâce au très bon ramasse-miettes et à la
bibliothèque de structures de données persistantes très performantes
d’OCaml. Enfin, nous avons largement bénéficié du système de modules
d’OCaml, en particulier les foncteurs et les modules récursifs, pour
concevoir un code très modulaire, maintenable et facilement extensible.
Au final, OCaml nous a permis de concevoir un solveur SMT aussi
performant que CVC4 ou Z3 pour la preuve de programmes, mais avec un
nombre de lignes de code divisé par trois ou quatre. Bien sûr, cela ne
garantit pas que Alt-Ergo ait zéro bugs, mais cela nous aide beaucoup à
mettre le doigt dessus quand quelqu’un en trouve.
*“OCaml nous a permis de concevoir un solveur SMT aussi performant que CVC4 ou Z3 pour la
preuve de programmes, mais avec un nombre de lignes de code divisé par
trois ou quatre.“*
**Quel est ton avis sur les solveurs SMT et l’état de l’art SMT actuel ?**
Les solveurs SMT sont aujourd’hui incontournables dans le domaine de
l’ingénierie du logiciel. On les trouve aussi bien dans des outils de
preuve, de test, de model checking, d’interprétation abstraite ou encore
de typage. La principale raison de ce succès est qu’ils sont de plus en
plus efficaces et les théories sous-jacentes sont très expressives.
C’est un domaine de recherche très concurrentiel entre les meilleures
universités ou laboratoires du monde et de grandes entreprises en
informatique. Mais la marge de progression de ces outils est encore très
grande, en particulier dans le domaine de l’arithmétique non linéaire
où la demande des utilisateurs est de plus en plus forte. Pour le
moment, un de mes objectifs en recherche est de combiner les outils de
Model Checking avec ceux de preuve de programmes. Ces deux familles
d’outils reposent sur les SMT et elles devraient se compléter pour
offrir des outils de vérification encore plus automatiques.
**Quelles applications les techniques SMT et Alt-Ergo peuvent-elles avoir dans l’industrie ?**
Les techniques SMT peuvent être utilisées partout où les méthodes
formelles peuvent être utiles. Par exemple (mais cette liste est loin
d’être exhaustive), pour vérifier la sûreté de logiciels critiques dans
le domaine de l’embarqué, pour trouver des failles de sécurité dans les
systèmes informatiques ou pour résoudre des problèmes de planification.
On les trouve également dans le domaine de l’intelligence artificielle
où il est crucial de garantir la stabilité des réseaux de neurones mais
aussi de produire des explications formelles sur leurs résultats.
**Tu as été amené à travailler sur le Model Checking, peux-tu
nous parler des liens entre Model Checking et SMT et de son utilisation
actuelle ?**
Le Model Checking consiste à vérifier que tous les états possibles
d’un système respectent bien certaines propriétés, et ce quelles que
soient les données en entrée. C’est un problème difficile car certains
systèmes (microprocesseurs par ex.) peuvent avoir des centaines de
millions d’états. Pour passer à l’échelle, les model checkers
implémentent des algorithmes très perfectionnés pour visiter ces états
rapidement, en les stockant d’une manière très compacte. Cependant,
cette technique atteint ses limites quand les valeurs prises en entrée
sont non bornées ou quand le nombre de composants du système n’est pas
connu. Pensez aux algorithmes de routage d’Internet où on ne connaît pas
le nombre de machines sur le réseau, ces algorithmes doivent être
corrects, quel que soit ce nombre de machines. C’est là que les solveurs
SMT entrent en jeu. En utilisant des formules logiques, on peut
représenter des ensembles d’états de taille arbitraire. Visiter les
états d’un système consiste alors à calculer les formules qui
représentent ces états. Vérifier que les états respectent une propriété
revient à prouver que les formules qui représentent des états impliquent
la propriété voulue, etc. Tout dans le Model Checking repose donc sur
des formules logiques et les solveurs SMT sont évidemment là pour
raisonner sur ces formules.
(executable (executable
(public_name server) (public_name server)
(modules content server blog blog_content feed error meta) (modules content server blog blog_content feed error meta urls)
(libraries dream omd ubase markup lambdasoup ez_file ez_subst)) (libraries dream omd ubase markup lambdasoup ez_file ez_subst))
(rule (rule
......
...@@ -83,7 +83,7 @@ let blog_asset_loader _root path _request = ...@@ -83,7 +83,7 @@ let blog_asset_loader _root path _request =
| None -> Dream.empty `Not_Found | None -> Dream.empty `Not_Found
| Some asset -> Dream.respond asset | Some asset -> Dream.respond asset
let feed_loader _root _path _loader = Dream.respond Feed.generate let feed_loader _root _path _request = Dream.respond Feed.generate
let page path = let page path =
match read_content (path ^ ".md") with match read_content (path ^ ".md") with
...@@ -107,6 +107,19 @@ let given_article path = ...@@ -107,6 +107,19 @@ let given_article path =
, article.authors ) , article.authors )
end end
let redirect_loader _root path request =
begin
match Hashtbl.find_opt Urls.redirection_hash ("/" ^ path),
Hashtbl.find_opt Urls.redirection_hash ("/" ^ path ^ "/")
with
| None, None ->
Dream.empty `Not_Found
| None, Some new_url | Some new_url, None ->
Dream.redirect ~status:(`Moved_Permanently) request new_url
| Some _same_new_url, Some new_url ->
Dream.redirect ~status:(`Moved_Permanently) request new_url
end
let title content = let title content =
let open Soup in let open Soup in
try try
...@@ -185,5 +198,49 @@ let () = ...@@ -185,5 +198,49 @@ let () =
| Some content -> | Some content ->
let title = title content in let title = title content in
Dream.html (render_unsafe ~title ~content ()) ) Dream.html (render_unsafe ~title ~content ()) )
(* REDIRECTS ROOT PAGES WITH TRAILING / *)
; Dream.get "/:something/" (fun request ->
let something = Dream.param "something" request in
let url = Format.sprintf "%s/" something in
redirect_loader "" url request)
(* REDIRECTS FR STATIC PAGES WITH TRAILING / *)
; Dream.get "/fr/:something/" (fun request ->
let something = Dream.param "something" request in
let url = Format.sprintf "fr/%s/" something in
redirect_loader "" url request)
(* REDIRECTS OLD CATEGORIES PAGES WITH TRAILING / *)
; Dream.get "/category/:something/" (fun request ->
let something = Dream.param "something" request in
let url = Format.sprintf "category/%s/" something in
redirect_loader "" url request)
(* REDIRECTS FR STATIC PAGES WITH TRAILING / *)
; Dream.get "/fr/:something/:something_else/" (fun request ->
let something = Dream.param "something" request in
let something_else = Dream.param "something_else" request in
let url = Format.sprintf "fr/%s/%s/" something something_else in
redirect_loader "" url request)
(* REDIRECT FR AUTHOR PAGES WITH TRAILING / *)
; Dream.get "/fr/author/:author/" (fun request ->
let author = Dream.param "author" request in
let url = Format.sprintf "fr/author/%s/" author in
redirect_loader "" url request)
(* REDIRECTS BLOG ARTICLES WITH TRAILING / *)
; Dream.get "/:year/:month/:day/:article/" (fun request ->
let year = Dream.param "year" request in
let month = Dream.param "month" request in
let day = Dream.param "day" request in
let article = Dream.param "article" request in
let url = Format.sprintf "%s/%s/%s/%s/" year month day article in
redirect_loader "" url request)
(* REDIRECTS FR BLOG ARTICLES WITH TRAILING / *)
; Dream.get "/fr/:year/:month/:day/:article/" (fun request ->
let year = Dream.param "year" request in
let month = Dream.param "month" request in
let day = Dream.param "day" request in
let article = Dream.param "article" request in
let url = Format.sprintf "fr/%s/%s/%s/%s/" year month day article in
redirect_loader "" url request)
(* REDIRECTS ALL URLS WITH NO TAILING / *)
; Dream.get "/**" (Dream.static ~loader:redirect_loader "")
] ]
@@ Dream.not_found @@ Dream.not_found
...@@ -19,36 +19,42 @@ let old_to_new = ...@@ -19,36 +19,42 @@ let old_to_new =
] ]
(* PAGES STATIQUES FR *) (* PAGES STATIQUES FR *)
@ [ ("/fr/accueil/", "/index") @ [ ("/fr/accueil/", "/index")
; ("/fr/accueil/#equipe/", "/team") ; ("/fr/accueil", "/index")
; ("/fr/recrutement-ocamlpro", "/index")
; ("/fr/recrutement-ocamlpro/", "/index") ; ("/fr/recrutement-ocamlpro/", "/index")
; ("/fr/lequipe-ocamlpro", "/team") ; ("/fr/lequipe-ocamlpro", "/team")
; ("/fr/lequipe-ocamlpro/", "/team") ; ("/fr/lequipe-ocamlpro/", "/team")
; ("/fr/accueil/#expertise/", "/index") ; ("/fr/accueil/formations", "/index")
; ("/fr/accueil/formations/", "/index") ; ("/fr/accueil/formations/", "/index")
; ( "/fr/formation-maitriser-opam-et-les-outils-ocaml" ; ( "/fr/formation-maitriser-opam-et-les-outils-ocaml"
, "/course_mastering_opam_ocaml_tools" ) , "/course_mastering_opam_ocaml_tools" )
; ( "/fr/formation-maitriser-opam-et-les-outils-ocaml/" ; ( "/fr/formation-maitriser-opam-et-les-outils-ocaml/"
, "/course_mastering_opam_ocaml_tools" ) , "/course_mastering_opam_ocaml_tools" )
; ("/fr/mentions-legales", "/legal-notice") ; ("/fr/mentions-legales", "/legal-notice")
; ("/fr/accueil/#prototypage", "/startup_studio") ; ("/fr/mentions-legales/", "/legal-notice")
; ("/fr/formation-expert-ocaml", "/course_ocaml_expert")
; ("/fr/formation-expert-ocaml/", "/course_ocaml_expert") ; ("/fr/formation-expert-ocaml/", "/course_ocaml_expert")
; ("/fr/formation-rust", "/course_rust_vocational_training")
; ("/fr/formation-rust/", "/course_rust_vocational_training") ; ("/fr/formation-rust/", "/course_rust_vocational_training")
; ("/fr/recherche-developpement", "/research-and-development")
; ("/fr/recherche-developpement/", "/research-and-development") ; ("/fr/recherche-developpement/", "/research-and-development")
; ("/fr/formation-passer-a-ocaml", "/course_ocaml_development") ; ("/fr/formation-passer-a-ocaml", "/course_ocaml_development")
; ("/fr/formation-passer-a-ocaml/", "/course_ocaml_development") ; ("/fr/formation-passer-a-ocaml/", "/course_ocaml_development")
; ("/fr/formation-optimiser-du-code-ocaml", "/course_ocaml_code_opti")
; ("/fr/formation-optimiser-du-code-ocaml/", "/course_ocaml_code_opti") ; ("/fr/formation-optimiser-du-code-ocaml/", "/course_ocaml_code_opti")
] ]
(* STATIC PAGES *) (* STATIC PAGES *)
@ [ ("/", "/index") @ [ ("/", "/index")
; ("/team", "/team") ; ("/team", "/team")
; ("/#team", "/team") ; ("/team/", "/team")
; ("/#expertise", "/index") ; ("/training-ocamlpro", "/")
; ("/training-ocamlpro/", "/index") ; ("/training-ocamlpro/", "/")
; ("/legal-notice", "/legal-notice") ; ("/legal-notice", "/legal-notice")
; ("/#prototyping", "/startup_studio") ; ("/legal-notice/", "/legal-notice")
; ("/mentions-legales", "/legal-notice") ; ("/mentions-legales", "/legal-notice")
; ("/mentions-legales/", "/legal-notice")
; ("/research-and-development", "/research-and-development")
; ("/research-and-development/", "/research-and-development") ; ("/research-and-development/", "/research-and-development")
; ("/rust-vocational-training-2/", "/course_rust_vocational_training")
] ]
(* COURSE PAGES *) (* COURSE PAGES *)
@ [ ( "/course-mastering-opam-ocamls-tooling/" @ [ ( "/course-mastering-opam-ocamls-tooling/"
...@@ -71,7 +77,7 @@ let old_to_new = ...@@ -71,7 +77,7 @@ let old_to_new =
; ("/fr/author/leob/", "/blog/authors/leo_boitel") ; ("/fr/author/leob/", "/blog/authors/leo_boitel")
; ("/fr/author/muriel/", "/blog/authors/muriel") ; ("/fr/author/muriel/", "/blog/authors/muriel")
; ("/fr/author/vincent/", "?") ; ("/fr/author/vincent/", "?")
; ("/fr/author/alain/", "?") ; ("/fr/author/alain/", "/blog/authors/alain_mebsout")
] ]
(*CATEGORIES*) (*CATEGORIES*)
@ [ ("/category/ocaml-opam-tooling/", "/blog/category/tooling") @ [ ("/category/ocaml-opam-tooling/", "/blog/category/tooling")
...@@ -257,9 +263,11 @@ let old_to_new = ...@@ -257,9 +263,11 @@ let old_to_new =
, "/blog/2020_04_21_opam_2.1.0_alpha_is_here" ) , "/blog/2020_04_21_opam_2.1.0_alpha_is_here" )
; ( "/2020/05/19/ocaml-solidity-parser-with-menhir/" ; ( "/2020/05/19/ocaml-solidity-parser-with-menhir/"
, "/blog/2020_05_19_ocaml_solidity_parser_with_menhir" ) , "/blog/2020_05_19_ocaml_solidity_parser_with_menhir" )
; ("/fr/2020/06/01/tutoriel-format/", "/2020_06_01_tutoriel_format") ; ("/fr/2020/06/01/tutoriel-format/", "/blog/2020_06_01_tutoriel_format")
; ( "/2020/06/05/interview-sylvain-conchon-joins-ocamlpro/" ; ( "/2020/06/05/interview-sylvain-conchon-joins-ocamlpro/"
, "/blog/2020_06_05_interview_sylvain_conchon_joins_ocamlpro" ) , "/blog/2020_06_05_interview_sylvain_conchon_joins_ocamlpro" )
; ( "/fr/2020/06/05/interview-sylvain-conchon-rejoint-ocamlpro/"
, "/blog/2020_06_05_fr_interview_sylvain_conchon_rejoint_ocamlpro" )
; ( "/2020/06/09/a-dune-love-story-from-liquidity-to-love/" ; ( "/2020/06/09/a-dune-love-story-from-liquidity-to-love/"
, "/blog/2020_06_09_a_dune_love_story_from_liquidity_to_love" ) , "/blog/2020_06_09_a_dune_love_story_from_liquidity_to_love" )
; ( "/2020/09/24/rehabilitating-packs-using-functors-and-recursivity-part-1/" ; ( "/2020/09/24/rehabilitating-packs-using-functors-and-recursivity-part-1/"
...@@ -279,7 +287,7 @@ let old_to_new = ...@@ -279,7 +287,7 @@ let old_to_new =
, "/blog/2021_04_29_reunion_annuelle_du_club_des_utilisateurs_dalt_ergo_2021" , "/blog/2021_04_29_reunion_annuelle_du_club_des_utilisateurs_dalt_ergo_2021"
) )
; ( "/2021/05/06/tutorial-format-module-of-ocaml/" ; ( "/2021/05/06/tutorial-format-module-of-ocaml/"
, "/2021_05_06_tutorial_format_module_of_ocaml" ) , "/blog/2021_05_06_tutorial_format_module_of_ocaml" )
; ( "/2021/07/16/detecting-identity-functions-in-flambda/" ; ( "/2021/07/16/detecting-identity-functions-in-flambda/"
, "/blog/2021_07_16_detecting_identity_functions_in_flambda" ) , "/blog/2021_07_16_detecting_identity_functions_in_flambda" )
; ("/2021/08/05/opam-2-0-9-release/", "/blog/2021_08_03_opam_2.0.9_release") ; ("/2021/08/05/opam-2-0-9-release/", "/blog/2021_08_03_opam_2.0.9_release")
...@@ -310,3 +318,14 @@ let old_to_new = ...@@ -310,3 +318,14 @@ let old_to_new =
("https:/files.ocamlpro.com/files/tuareg-mode.pdf", "/") ("https:/files.ocamlpro.com/files/tuareg-mode.pdf", "/")
; ("/files/try-alt-ergo.why", "/") ; ("/files/try-alt-ergo.why", "/")
] ]
let redirection_hash =
let tbl = Hashtbl.create 512 in
List.iter (fun (oldu, newu) ->
let collision =
Hashtbl.find_opt tbl oldu in
match collision with
| None -> Hashtbl.add tbl oldu newu
| Some _new_url -> Blog.error (Format.sprintf "%s has a duplicate redirection." oldu)
) old_to_new;
tbl
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment